summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Widenius <monty@askmonty.org>2011-02-22 11:15:47 +0200
committerMichael Widenius <monty@askmonty.org>2011-02-22 11:15:47 +0200
commit4ad6d6b6c9e551d84fe727d1f48edc8722098bfd (patch)
treee305bc182a35eaf41802e4cc51a61bfd5bde5cd2
parent38e3787af4ac87274c7ef0e632a97aa1217e284a (diff)
parent2c7d6f12ee10903c3002bba42ee15abc2b89fa8e (diff)
downloadmariadb-git-4ad6d6b6c9e551d84fe727d1f48edc8722098bfd.tar.gz
Merge in new handler and handlersocket code into 5.3 main
-rw-r--r--.bzrignore1
-rwxr-xr-xBUILD/compile-pentium645
-rw-r--r--README31
-rw-r--r--include/heap.h4
-rw-r--r--include/thr_alarm.h1
-rw-r--r--include/thr_lock.h4
-rw-r--r--mysql-test/Makefile.am1
-rw-r--r--mysql-test/README19
-rw-r--r--mysql-test/extra/rpl_tests/rpl_stop_slave.test1
-rw-r--r--mysql-test/include/check_no_concurrent_insert.inc2
-rw-r--r--mysql-test/include/have_xtradb.inc9
-rw-r--r--mysql-test/include/have_xtradb.opt2
-rw-r--r--mysql-test/include/long_test.inc4
-rw-r--r--mysql-test/lib/mtr_cases.pm19
-rwxr-xr-xmysql-test/mysql-test-run.pl10
-rw-r--r--mysql-test/r/lock_multi.result7
-rw-r--r--mysql-test/r/log_tables.result8
-rw-r--r--mysql-test/r/plugin.result6
-rw-r--r--mysql-test/r/sp-threads.result2
-rw-r--r--mysql-test/r/subselect.result11
-rw-r--r--mysql-test/r/subselect_no_mat.result11
-rw-r--r--mysql-test/r/subselect_no_opts.result11
-rw-r--r--mysql-test/r/subselect_no_semijoin.result11
-rw-r--r--mysql-test/r/table_elim.result31
-rw-r--r--mysql-test/r/table_options.result46
-rw-r--r--mysql-test/suite/binlog/t/binlog_stm_row.test2
-rw-r--r--mysql-test/suite/federated/federated_debug.test1
-rw-r--r--mysql-test/suite/funcs_1/datadict/processlist_val.inc10
-rw-r--r--mysql-test/suite/handler/aria.result801
-rw-r--r--mysql-test/suite/handler/aria.test82
-rw-r--r--mysql-test/suite/handler/handler.inc (renamed from mysql-test/include/handler.inc)527
-rw-r--r--mysql-test/suite/handler/heap.result786
-rw-r--r--mysql-test/suite/handler/heap.test88
-rw-r--r--mysql-test/suite/handler/init.inc33
-rw-r--r--mysql-test/suite/handler/innodb.result (renamed from mysql-test/r/handler_innodb.result)571
-rw-r--r--mysql-test/suite/handler/innodb.test28
-rw-r--r--mysql-test/suite/handler/interface.result261
-rw-r--r--mysql-test/suite/handler/interface.test313
-rw-r--r--mysql-test/suite/handler/myisam.result (renamed from mysql-test/r/handler_myisam.result)580
-rw-r--r--mysql-test/suite/handler/myisam.test (renamed from mysql-test/t/handler_myisam.test)24
-rw-r--r--mysql-test/suite/innodb/r/innodb_mysql.result8
-rw-r--r--mysql-test/suite/innodb/t/innodb_mysql.test11
-rw-r--r--mysql-test/suite/maria/r/maria.result18
-rw-r--r--mysql-test/suite/maria/r/maria3.result2
-rw-r--r--mysql-test/suite/maria/t/maria-recovery-rtree-ft.test1
-rw-r--r--mysql-test/suite/maria/t/maria.test18
-rw-r--r--mysql-test/suite/parts/t/partition_alter2_1_myisam.test2
-rw-r--r--mysql-test/suite/parts/t/partition_alter2_2_myisam.test2
-rw-r--r--mysql-test/suite/parts/t/partition_basic_innodb.test2
-rw-r--r--mysql-test/suite/parts/t/partition_decimal_myisam.test2
-rw-r--r--mysql-test/suite/parts/t/partition_float_myisam.test2
-rw-r--r--mysql-test/suite/parts/t/partition_int_myisam.test2
-rw-r--r--mysql-test/suite/pbxt/t/lock_multi.test16
-rw-r--r--mysql-test/suite/rpl/r/rpl_stop_slave.result4
-rw-r--r--mysql-test/suite/rpl/t/rpl_deadlock_innodb.test1
-rw-r--r--mysql-test/suite/rpl/t/rpl_row_sp003.test1
-rw-r--r--mysql-test/suite/sys_vars/r/concurrent_insert_func.result4
-rw-r--r--mysql-test/suite/sys_vars/t/concurrent_insert_func.test4
-rw-r--r--mysql-test/suite/sys_vars/t/delayed_insert_limit_func.test4
-rw-r--r--mysql-test/suite/sys_vars/t/query_cache_wlock_invalidate_func.test2
-rw-r--r--mysql-test/suite/sys_vars/t/sql_low_priority_updates_func.test8
-rw-r--r--mysql-test/suite/vcol/inc/vcol_unsupported_storage_engines.inc6
-rw-r--r--mysql-test/suite/vcol/r/vcol_archive.result6
-rw-r--r--mysql-test/suite/vcol/r/vcol_blackhole.result6
-rw-r--r--mysql-test/suite/vcol/r/vcol_csv.result4
-rw-r--r--mysql-test/suite/vcol/r/vcol_memory.result6
-rw-r--r--mysql-test/suite/vcol/r/vcol_merge.result2
-rw-r--r--mysql-test/suite/vcol/r/vcol_non_stored_columns_innodb.result4
-rw-r--r--mysql-test/suite/vcol/r/vcol_non_stored_columns_myisam.result4
-rw-r--r--mysql-test/suite/vcol/t/rpl_vcol.test2
-rw-r--r--mysql-test/suite/vcol/t/vcol_blocked_sql_funcs_innodb.test2
-rw-r--r--mysql-test/suite/vcol/t/vcol_column_def_options_innodb.test2
-rw-r--r--mysql-test/suite/vcol/t/vcol_csv.test8
-rw-r--r--mysql-test/suite/vcol/t/vcol_handler_innodb.test2
-rw-r--r--mysql-test/suite/vcol/t/vcol_ins_upd_innodb.test2
-rw-r--r--mysql-test/suite/vcol/t/vcol_keys_innodb.test2
-rw-r--r--mysql-test/suite/vcol/t/vcol_merge.test2
-rw-r--r--mysql-test/suite/vcol/t/vcol_non_stored_columns_innodb.test2
-rw-r--r--mysql-test/suite/vcol/t/vcol_partition_innodb.test2
-rw-r--r--mysql-test/suite/vcol/t/vcol_select_innodb.test2
-rw-r--r--mysql-test/suite/vcol/t/vcol_supported_sql_funcs_innodb.test2
-rw-r--r--mysql-test/suite/vcol/t/vcol_trigger_sp_innodb.test2
-rw-r--r--mysql-test/suite/vcol/t/vcol_view_innodb.test2
-rw-r--r--mysql-test/t/handler_innodb.test20
-rw-r--r--mysql-test/t/insert_notembedded.test2
-rw-r--r--mysql-test/t/join_outer.test2
-rw-r--r--mysql-test/t/lock_multi.test38
-rw-r--r--mysql-test/t/merge-big.test2
-rw-r--r--mysql-test/t/multi_update.test8
-rw-r--r--mysql-test/t/multi_update2.test2
-rw-r--r--mysql-test/t/pool_of_threads.test1
-rw-r--r--mysql-test/t/query_cache.test1
-rw-r--r--mysql-test/t/query_cache_28249.test4
-rw-r--r--mysql-test/t/sp_notembedded.test2
-rw-r--r--mysql-test/t/sp_sync.test2
-rw-r--r--mysql-test/t/status.test2
-rw-r--r--mysql-test/t/subselect.test15
-rw-r--r--mysql-test/t/table_elim.test32
-rw-r--r--mysql-test/t/trigger_notembedded.test2
-rw-r--r--mysys/thr_alarm.c26
-rw-r--r--mysys/thr_lock.c11
-rw-r--r--mysys/tree.c4
-rw-r--r--plugin/handler_socket/AUTHORS19
-rw-r--r--plugin/handler_socket/ChangeLog19
-rw-r--r--plugin/handler_socket/Makefile.am88
-rw-r--r--plugin/handler_socket/README82
-rwxr-xr-xplugin/handler_socket/autogen.sh117
-rw-r--r--plugin/handler_socket/client/Makefile.am14
-rw-r--r--plugin/handler_socket/client/hsclient.cpp88
-rwxr-xr-xplugin/handler_socket/client/hspool_test.pl224
-rw-r--r--plugin/handler_socket/client/hstest.cpp1494
-rwxr-xr-xplugin/handler_socket/client/hstest.pl228
-rwxr-xr-xplugin/handler_socket/client/hstest_hs.sh4
-rwxr-xr-xplugin/handler_socket/client/hstest_hs_more50.sh4
-rwxr-xr-xplugin/handler_socket/client/hstest_md.sh7
-rwxr-xr-xplugin/handler_socket/client/hstest_my.sh3
-rwxr-xr-xplugin/handler_socket/client/hstest_my_more50.sh3
-rw-r--r--plugin/handler_socket/configure.ac134
-rw-r--r--plugin/handler_socket/docs-en/about-handlersocket.en.txt72
-rw-r--r--plugin/handler_socket/docs-en/configuration-options.en.txt87
-rw-r--r--plugin/handler_socket/docs-en/installation.en.txt91
-rw-r--r--plugin/handler_socket/docs-en/perl-client.en.txt126
-rw-r--r--plugin/handler_socket/docs-en/protocol.en.txt148
-rw-r--r--plugin/handler_socket/docs-ja/about-handlersocket.ja.txt51
-rw-r--r--plugin/handler_socket/docs-ja/installation.ja.txt87
-rw-r--r--plugin/handler_socket/docs-ja/perl-client.ja.txt118
-rw-r--r--plugin/handler_socket/docs-ja/protocol.ja.txt94
-rw-r--r--plugin/handler_socket/handlersocket/COPYRIGHT.txt27
-rw-r--r--plugin/handler_socket/handlersocket/Makefile.am11
-rw-r--r--plugin/handler_socket/handlersocket/Makefile.plain.template31
-rw-r--r--plugin/handler_socket/handlersocket/database.cpp1143
-rw-r--r--plugin/handler_socket/handlersocket/database.hpp130
-rw-r--r--plugin/handler_socket/handlersocket/handlersocket.cpp216
-rw-r--r--plugin/handler_socket/handlersocket/handlersocket.spec.template29
-rw-r--r--plugin/handler_socket/handlersocket/hstcpsvr.cpp149
-rw-r--r--plugin/handler_socket/handlersocket/hstcpsvr.hpp58
-rw-r--r--plugin/handler_socket/handlersocket/hstcpsvr_worker.cpp896
-rw-r--r--plugin/handler_socket/handlersocket/hstcpsvr_worker.hpp35
-rw-r--r--plugin/handler_socket/handlersocket/mysql_incl.hpp49
-rw-r--r--plugin/handler_socket/libhsclient/COPYRIGHT.txt27
-rw-r--r--plugin/handler_socket/libhsclient/Makefile.am13
-rw-r--r--plugin/handler_socket/libhsclient/Makefile.plain27
-rw-r--r--plugin/handler_socket/libhsclient/allocator.hpp37
-rw-r--r--plugin/handler_socket/libhsclient/auto_addrinfo.hpp50
-rw-r--r--plugin/handler_socket/libhsclient/auto_file.hpp64
-rw-r--r--plugin/handler_socket/libhsclient/auto_ptrcontainer.hpp67
-rw-r--r--plugin/handler_socket/libhsclient/config.cpp67
-rw-r--r--plugin/handler_socket/libhsclient/config.hpp32
-rw-r--r--plugin/handler_socket/libhsclient/escape.cpp117
-rw-r--r--plugin/handler_socket/libhsclient/escape.hpp65
-rw-r--r--plugin/handler_socket/libhsclient/fatal.cpp36
-rw-r--r--plugin/handler_socket/libhsclient/fatal.hpp22
-rw-r--r--plugin/handler_socket/libhsclient/hstcpcli.cpp453
-rw-r--r--plugin/handler_socket/libhsclient/hstcpcli.hpp59
-rw-r--r--plugin/handler_socket/libhsclient/libhsclient.spec.template39
-rw-r--r--plugin/handler_socket/libhsclient/mutex.hpp51
-rw-r--r--plugin/handler_socket/libhsclient/socket.cpp186
-rw-r--r--plugin/handler_socket/libhsclient/socket.hpp51
-rw-r--r--plugin/handler_socket/libhsclient/string_buffer.hpp118
-rw-r--r--plugin/handler_socket/libhsclient/string_ref.hpp63
-rw-r--r--plugin/handler_socket/libhsclient/string_util.cpp182
-rw-r--r--plugin/handler_socket/libhsclient/string_util.hpp53
-rw-r--r--plugin/handler_socket/libhsclient/thread.hpp84
-rw-r--r--plugin/handler_socket/libhsclient/util.hpp25
-rw-r--r--plugin/handler_socket/misc/microbench-hs.log130
-rw-r--r--plugin/handler_socket/misc/microbench-my.log125
-rw-r--r--plugin/handler_socket/perl-Net-HandlerSocket/COPYRIGHT.txt27
-rw-r--r--plugin/handler_socket/perl-Net-HandlerSocket/Changes6
-rw-r--r--plugin/handler_socket/perl-Net-HandlerSocket/HandlerSocket.xs577
-rw-r--r--plugin/handler_socket/perl-Net-HandlerSocket/MANIFEST8
-rw-r--r--plugin/handler_socket/perl-Net-HandlerSocket/Makefile.PL18
-rw-r--r--plugin/handler_socket/perl-Net-HandlerSocket/Makefile.PL.installed20
-rw-r--r--plugin/handler_socket/perl-Net-HandlerSocket/README30
-rw-r--r--plugin/handler_socket/perl-Net-HandlerSocket/lib/Net/HandlerSocket.pm68
-rwxr-xr-xplugin/handler_socket/perl-Net-HandlerSocket/lib/Net/HandlerSocket/Pool.pm362
-rw-r--r--plugin/handler_socket/perl-Net-HandlerSocket/perl-Net-HandlerSocket.spec.template127
-rw-r--r--plugin/handler_socket/perl-Net-HandlerSocket/ppport.h6375
-rw-r--r--plugin/handler_socket/perl-Net-HandlerSocket/t/HandlerSocket.t15
-rw-r--r--plugin/handler_socket/plug.in19
-rw-r--r--plugin/handler_socket/regtest/common/binary_my.cnf4
-rw-r--r--plugin/handler_socket/regtest/common/compat.sh29
-rw-r--r--plugin/handler_socket/regtest/common/hstest.pm62
-rwxr-xr-xplugin/handler_socket/regtest/test_01_lib/run.sh27
-rw-r--r--plugin/handler_socket/regtest/test_01_lib/test01.expected100
-rw-r--r--plugin/handler_socket/regtest/test_01_lib/test01.pl38
-rw-r--r--plugin/handler_socket/regtest/test_01_lib/test02.expected100
-rw-r--r--plugin/handler_socket/regtest/test_01_lib/test02.pl49
-rw-r--r--plugin/handler_socket/regtest/test_01_lib/test03.expected771
-rw-r--r--plugin/handler_socket/regtest/test_01_lib/test03.pl61
-rw-r--r--plugin/handler_socket/regtest/test_01_lib/test04.expectedbin0 -> 9589 bytes
-rw-r--r--plugin/handler_socket/regtest/test_01_lib/test04.pl63
-rw-r--r--plugin/handler_socket/regtest/test_01_lib/test05.expected771
-rw-r--r--plugin/handler_socket/regtest/test_01_lib/test05.pl59
-rw-r--r--plugin/handler_socket/regtest/test_01_lib/test06.expected644
-rw-r--r--plugin/handler_socket/regtest/test_01_lib/test06.pl90
-rw-r--r--plugin/handler_socket/regtest/test_01_lib/test07.expected304
-rw-r--r--plugin/handler_socket/regtest/test_01_lib/test07.pl98
-rw-r--r--plugin/handler_socket/regtest/test_01_lib/test08.expected2
-rw-r--r--plugin/handler_socket/regtest/test_01_lib/test08.pl48
-rw-r--r--plugin/handler_socket/regtest/test_01_lib/test09.expected12
-rw-r--r--plugin/handler_socket/regtest/test_01_lib/test09.pl67
-rw-r--r--plugin/handler_socket/regtest/test_01_lib/test10.expected771
-rw-r--r--plugin/handler_socket/regtest/test_01_lib/test10.pl93
-rw-r--r--plugin/handler_socket/regtest/test_01_lib/test11.expected37
-rw-r--r--plugin/handler_socket/regtest/test_01_lib/test11.pl112
-rw-r--r--plugin/handler_socket/regtest/test_01_lib/test12.expected273
-rw-r--r--plugin/handler_socket/regtest/test_01_lib/test12.pl134
-rw-r--r--plugin/handler_socket/regtest/test_01_lib/test13.expected52
-rw-r--r--plugin/handler_socket/regtest/test_01_lib/test13.pl86
-rw-r--r--scripts/mysql_convert_table_format.sh2
-rw-r--r--scripts/mysql_install_db.sh12
-rw-r--r--sql-common/client.c2
-rw-r--r--sql/Makefile.am2
-rw-r--r--sql/ha_partition.h1
-rw-r--r--sql/handler.cc5
-rw-r--r--sql/handler.h25
-rw-r--r--sql/item.h13
-rw-r--r--sql/item_func.cc15
-rw-r--r--sql/item_func.h1
-rw-r--r--sql/lock.cc303
-rw-r--r--sql/mysql_priv.h22
-rw-r--r--sql/mysqld.cc11
-rw-r--r--sql/net_serv.cc6
-rw-r--r--sql/share/errmsg.txt6
-rw-r--r--sql/sql_base.cc1
-rw-r--r--sql/sql_class.cc3
-rw-r--r--sql/sql_class.h2
-rw-r--r--sql/sql_db.cc1
-rw-r--r--sql/sql_delete.cc1
-rw-r--r--sql/sql_handler.cc632
-rw-r--r--sql/sql_handler.h61
-rw-r--r--sql/sql_parse.cc12
-rw-r--r--sql/sql_prepare.cc58
-rw-r--r--sql/sql_rename.cc2
-rw-r--r--sql/sql_select.cc8
-rw-r--r--sql/sql_show.cc6
-rw-r--r--sql/sql_table.cc31
-rw-r--r--sql/sql_trigger.cc1
-rw-r--r--sql/table.cc13
-rw-r--r--sql/table.h2
-rw-r--r--storage/federated/ha_federated.cc2
-rw-r--r--storage/heap/ha_heap.cc13
-rw-r--r--storage/heap/ha_heap.h4
-rw-r--r--storage/heap/hp_clear.c2
-rw-r--r--storage/heap/hp_delete.c1
-rw-r--r--storage/heap/hp_rfirst.c15
-rw-r--r--storage/heap/hp_rkey.c1
-rw-r--r--storage/heap/hp_rlast.c8
-rw-r--r--storage/heap/hp_rnext.c1
-rw-r--r--storage/heap/hp_rprev.c1
-rw-r--r--storage/heap/hp_scan.c2
-rw-r--r--storage/heap/hp_update.c4
-rw-r--r--storage/heap/hp_write.c1
-rw-r--r--storage/ibmdb2i/db2i_ioBuffers.h2
-rw-r--r--storage/innodb_plugin/trx/trx0sys.c2
-rw-r--r--storage/maria/ha_maria.cc23
-rw-r--r--storage/maria/ha_maria.h1
-rw-r--r--storage/maria/ma_bitmap.c6
-rw-r--r--storage/maria/ma_blockrec.c84
-rw-r--r--storage/maria/ma_blockrec.h2
-rw-r--r--storage/maria/ma_check.c88
-rw-r--r--storage/maria/ma_extra.c4
-rw-r--r--storage/maria/ma_ft_boolean_search.c6
-rw-r--r--storage/maria/ma_open.c2
-rw-r--r--storage/maria/ma_pagecache.c54
-rw-r--r--storage/maria/ma_pagecache.h1
-rw-r--r--storage/maria/ma_recovery.c40
-rw-r--r--storage/maria/ma_recovery.h2
-rw-r--r--storage/maria/ma_recovery_util.c8
-rw-r--r--storage/maria/ma_rkey.c2
-rw-r--r--storage/maria/ma_rnext.c17
-rw-r--r--storage/maria/ma_rsame.c10
-rw-r--r--storage/maria/ma_rt_index.c1
-rw-r--r--storage/maria/ma_search.c17
-rw-r--r--storage/maria/ma_sort.c14
-rw-r--r--storage/maria/ma_unique.c17
-rw-r--r--storage/maria/maria_def.h1
-rw-r--r--storage/maria/maria_read_log.c67
-rw-r--r--storage/myisam/ha_myisam.cc1
-rw-r--r--storage/myisam/ha_myisam.h1
-rw-r--r--storage/myisammrg/ha_myisammrg.cc4
-rw-r--r--storage/ndb/include/ndbapi/NdbError.hpp2
-rw-r--r--storage/ndb/include/util/File.hpp2
-rw-r--r--storage/xtradb/handler/ha_innodb.cc2
-rw-r--r--storage/xtradb/trx/trx0sys.c2
285 files changed, 25763 insertions, 1791 deletions
diff --git a/.bzrignore b/.bzrignore
index f49e2f72922..2ba5ecd583f 100644
--- a/.bzrignore
+++ b/.bzrignore
@@ -1943,3 +1943,4 @@ storage/pbxt/bin/xtstat
libmysqld/sql_expression_cache.cc
mysql-test/mtr_command
scripts/convert-debug-for-diff
+plugin/handler_socket/client/hsclient
diff --git a/BUILD/compile-pentium64 b/BUILD/compile-pentium64
index 3a8fad51fea..01eb2adf88b 100755
--- a/BUILD/compile-pentium64
+++ b/BUILD/compile-pentium64
@@ -4,7 +4,10 @@ path=`dirname $0`
. "$path/SETUP.sh"
extra_flags="$pentium64_cflags $fast_cflags"
-extra_configs="$pentium_configs $static_link"
+# On CentOS/Fedora Core 10 amd64, there is system libz.so but not
+# libz.a, so need to use bundled zlib when building static
+# binary. Hence we use --with-zlib-dir=bundled
+extra_configs="$pentium_configs $static_link --with-zlib-dir=bundled"
CC="$CC --pipe"
strip=yes
diff --git a/README b/README
index 5b574864f96..e569b38200d 100644
--- a/README
+++ b/README
@@ -1291,3 +1291,34 @@ Use of any of this software is governed by the terms of the license below:
*/
***************************************************************************
+
+%%The following software may be included in this product:
+HandlerSocket plugin for MySQL
+
+Copyright (c) 2010 DeNA Co.,Ltd.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of DeNA Co.,Ltd. nor the names of its contributors
+ may be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY DeNA Co.,Ltd. "AS IS" AND ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+EVENT SHALL DeNA Co.,Ltd. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+***************************************************************************
diff --git a/include/heap.h b/include/heap.h
index 4a1c7d419ed..74bec8079f2 100644
--- a/include/heap.h
+++ b/include/heap.h
@@ -136,6 +136,8 @@ typedef struct st_heap_share
ulong min_records,max_records; /* Params to open */
ulonglong data_length,index_length,max_table_size;
uint key_stat_version; /* version to indicate insert/delete */
+ uint key_version; /* Updated on key change */
+ uint file_version; /* Update on clear */
uint records; /* records */
uint blength; /* records rounded up to 2^n */
uint deleted; /* Deleted records in database */
@@ -173,6 +175,8 @@ typedef struct st_heap_info
enum ha_rkey_function last_find_flag;
TREE_ELEMENT *parents[MAX_TREE_HEIGHT+1];
TREE_ELEMENT **last_pos;
+ uint key_version; /* Version at last read */
+ uint file_version; /* Version at scan */
uint lastkey_len;
my_bool implicit_emptied;
#ifdef THREAD
diff --git a/include/thr_alarm.h b/include/thr_alarm.h
index 99812ed808c..806735b2d38 100644
--- a/include/thr_alarm.h
+++ b/include/thr_alarm.h
@@ -88,6 +88,7 @@ typedef struct st_alarm {
extern uint thr_client_alarm;
extern pthread_t alarm_thread;
+extern my_bool my_disable_thr_alarm;
#define thr_alarm_init(A) (*(A))=0
#define thr_alarm_in_use(A) (*(A)!= 0)
diff --git a/include/thr_lock.h b/include/thr_lock.h
index 08cc8bd5408..ec24bbabddd 100644
--- a/include/thr_lock.h
+++ b/include/thr_lock.h
@@ -123,8 +123,10 @@ typedef struct st_thr_lock_data {
struct st_thr_lock *lock;
pthread_cond_t *cond;
void *status_param; /* Param to status functions */
- void *debug_print_param;
enum thr_lock_type type;
+
+ enum thr_lock_type org_type; /* Cache for MariaDB */
+ void *debug_print_param; /* For error messages */
uint priority;
} THR_LOCK_DATA;
diff --git a/mysql-test/Makefile.am b/mysql-test/Makefile.am
index cb34bab7ab1..ce0adc366a6 100644
--- a/mysql-test/Makefile.am
+++ b/mysql-test/Makefile.am
@@ -109,6 +109,7 @@ TEST_DIRS = t r include std_data std_data/parts collections \
suite/innodb suite/innodb/t suite/innodb/r suite/innodb/include \
suite/innodb_plugin suite/innodb_plugin/t suite/innodb_plugin/r \
suite/innodb_plugin/include \
+ suite/handler \
suite/engines suite/engines/funcs suite/engines/iuds suite/engines/rr_trx \
suite/engines/funcs/r suite/engines/funcs/t suite/engines/iuds/r \
suite/engines/iuds/t suite/engines/rr_trx/include suite/engines/rr_trx/r \
diff --git a/mysql-test/README b/mysql-test/README
index 3c8303ca070..d3be11278fc 100644
--- a/mysql-test/README
+++ b/mysql-test/README
@@ -4,13 +4,14 @@ this directory. It will fire up the newly built mysqld and test it.
Note that you do not have to have to do "make install", and you could
actually have a co-existing MySQL installation. The tests will not
-conflict with it.
+conflict with it. To run the test suite in a source directory, you
+must do make first.
All tests must pass. If one or more of them fail on your system, please
read the following manual section for instructions on how to report the
problem:
-http://dev.mysql.com/doc/mysql/en/mysql-test-suite.html
+http://kb.askmonty.org/v/reporting-bugs
If you want to use an already running MySQL server for specific tests,
use the --extern option to mysql-test-run. Please note that in this mode,
@@ -27,7 +28,6 @@ With no test cases named on the command line, mysql-test-run falls back
to the normal "non-extern" behavior. The reason for this is that some
tests cannot run with an external server.
-
You can create your own test cases. To create a test case, create a new
file in the t subdirectory using a text editor. The file should have a .test
extension. For example:
@@ -67,7 +67,12 @@ extension. For example:
edit the test result to the correct results so that we can verify
that the bug is corrected in future releases.
-To submit your test case, put your .test file and .result file(s) into
-a tar.gz archive, add a README that explains the problem, ftp the
-archive to ftp://support.mysql.com/pub/mysql/secret/ and send a mail
-to bugs@lists.mysql.com
+If you want to submit your test case you can send it
+to maria-developers@lists.launchpad.com or attach it to a bug report on
+https://bugs.launchpad.net/maria/.
+
+If the test case is really big or if it contains 'not public' data,
+then put your .test file and .result file(s) into a tar.gz archive,
+add a README that explains the problem, ftp the archive to
+ftp://ftp.askmonty.org/private and send a mail to
+https://bugs.launchpad.net/maria/ about it.
diff --git a/mysql-test/extra/rpl_tests/rpl_stop_slave.test b/mysql-test/extra/rpl_tests/rpl_stop_slave.test
index 7c88afe3532..5adfc99d59e 100644
--- a/mysql-test/extra/rpl_tests/rpl_stop_slave.test
+++ b/mysql-test/extra/rpl_tests/rpl_stop_slave.test
@@ -42,6 +42,7 @@ send STOP SLAVE SQL_THREAD;
connection slave1;
--echo # To resume slave SQL thread
SET DEBUG_SYNC= 'now SIGNAL signal.continue';
+SET DEBUG_SYNC= 'now WAIT_FOR signal.continued';
SET DEBUG_SYNC= 'RESET';
--echo
diff --git a/mysql-test/include/check_no_concurrent_insert.inc b/mysql-test/include/check_no_concurrent_insert.inc
index 6938c53fd16..f6a3d2052f5 100644
--- a/mysql-test/include/check_no_concurrent_insert.inc
+++ b/mysql-test/include/check_no_concurrent_insert.inc
@@ -43,7 +43,7 @@ connection default;
# of our statement.
let $wait_condition=
select count(*) = 1 from information_schema.processlist
- where state = "Locked" and info = "insert into $table (i) values (0)";
+ where state = "Table lock" and info = "insert into $table (i) values (0)";
--source include/wait_condition.inc
--disable_result_log
diff --git a/mysql-test/include/have_xtradb.inc b/mysql-test/include/have_xtradb.inc
index 6c2fc5155a9..3f3893e5642 100644
--- a/mysql-test/include/have_xtradb.inc
+++ b/mysql-test/include/have_xtradb.inc
@@ -1,4 +1,5 @@
-disable_query_log;
---require r/true.require
-SELECT (plugin_description LIKE '%xtradb%') AS `TRUE` FROM information_schema.plugins WHERE LOWER(plugin_name) = 'innodb' AND LOWER(plugin_status) = 'active';
-enable_query_log;
+if (!`SELECT count(*) FROM information_schema.plugins WHERE
+ plugin_name = 'innodb' AND plugin_status = 'active' AND
+ plugin_description LIKE '%xtradb%'`){
+ skip Need XtraDB engine;
+}
diff --git a/mysql-test/include/have_xtradb.opt b/mysql-test/include/have_xtradb.opt
new file mode 100644
index 00000000000..4fb96229a7b
--- /dev/null
+++ b/mysql-test/include/have_xtradb.opt
@@ -0,0 +1,2 @@
+--loose-innodb
+--plugin-load=$HA_XTRADB_SO
diff --git a/mysql-test/include/long_test.inc b/mysql-test/include/long_test.inc
new file mode 100644
index 00000000000..d9a3b338229
--- /dev/null
+++ b/mysql-test/include/long_test.inc
@@ -0,0 +1,4 @@
+# We use this --source include to mark a test as taking long to run.
+# We can use this to schedule such test early (to not be left with
+# only one or two long tests running, and rests of works idle), or to
+# run a quick test skipping long-running test cases.
diff --git a/mysql-test/lib/mtr_cases.pm b/mysql-test/lib/mtr_cases.pm
index c29a29723df..f11c76a0727 100644
--- a/mysql-test/lib/mtr_cases.pm
+++ b/mysql-test/lib/mtr_cases.pm
@@ -89,6 +89,20 @@ sub init_pattern {
}
+sub testcase_sort_order {
+ my ($a, $b, $sort_criteria)= @_;
+ my $a_sort_criteria= $sort_criteria->{$a->fullname()};
+ my $b_sort_criteria= $sort_criteria->{$b->fullname()};
+ my $res= $a_sort_criteria cmp $b_sort_criteria;
+ return $res if $res;
+ # Run slow tests first, trying to avoid getting stuck at the end
+ # with a slow test in one worker and the other workers idle.
+ return -1 if $a->{'long_test'} && !$b->{'long_test'};
+ return 1 if !$a->{'long_test'} && $b->{'long_test'};
+
+ return $a->fullname() cmp $b->fullname();
+}
+
##############################################################################
#
# Collect information about test cases to be run
@@ -177,9 +191,7 @@ sub collect_test_cases ($$$) {
$sort_criteria{$tinfo->fullname()} = join(" ", @criteria);
}
- @$cases = sort {
- $sort_criteria{$a->fullname()} . $a->fullname() cmp
- $sort_criteria{$b->fullname()} . $b->fullname() } @$cases;
+ @$cases = sort { testcase_sort_order($a, $b, \%sort_criteria) } @$cases;
# For debugging the sort-order
# foreach my $tinfo (@$cases)
@@ -1065,6 +1077,7 @@ my @tags=
["include/have_example_plugin.inc", "example_plugin_test", 1],
["include/have_oqgraph_engine.inc", "oqgraph_test", 1],
["include/have_ssl.inc", "need_ssl", 1],
+ ["include/long_test.inc", "long_test", 1],
);
diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl
index 59139ca2804..3df3c33ceb1 100755
--- a/mysql-test/mysql-test-run.pl
+++ b/mysql-test/mysql-test-run.pl
@@ -142,7 +142,7 @@ my $path_config_file; # The generated config file, var/my.cnf
# executables will be used by the test suite.
our $opt_vs_config = $ENV{'MTR_VS_CONFIG'};
-my $DEFAULT_SUITES="main,binlog,federated,rpl,maria,parts,innodb," .
+my $DEFAULT_SUITES="main,binlog,federated,rpl,maria,handler,parts,innodb," .
"innodb_plugin,percona,ndb,vcol,oqgraph,sphinx," .
"optimizer_unfixed_bugs";
my $opt_suites;
@@ -731,9 +731,11 @@ sub run_test_server ($$$) {
last;
}
- # Second best choice is the first that does not fulfill
- # any of the above conditions
- if (!defined $second_best){
+ # From secondary choices, we prefer to pick a 'long-running' test if
+ # possible; this helps avoid getting stuck with a few of those at the
+ # end of high --parallel runs, with most workers being idle.
+ if (!defined $second_best ||
+ ($t->{'long_test'} && !($tests->[$second_best]{'long_test'}))){
#mtr_report("Setting second_best to $i");
$second_best= $i;
}
diff --git a/mysql-test/r/lock_multi.result b/mysql-test/r/lock_multi.result
index d8768e802ea..3fc4de3da06 100644
--- a/mysql-test/r/lock_multi.result
+++ b/mysql-test/r/lock_multi.result
@@ -162,13 +162,6 @@ ERROR 70100: Query execution was interrupted
unlock tables;
drop table t1;
drop table if exists t1;
-create table t1 (a int) ENGINE=MEMORY;
---> client 2
-handler t1 open;
-ERROR HY000: Table storage engine for 't1' doesn't have this option
---> client 1
-drop table t1;
-drop table if exists t1;
create table t1 (i int);
connection: default
lock tables t1 write;
diff --git a/mysql-test/r/log_tables.result b/mysql-test/r/log_tables.result
index f8321520880..cee912ffb2e 100644
--- a/mysql-test/r/log_tables.result
+++ b/mysql-test/r/log_tables.result
@@ -248,13 +248,13 @@ set global slow_query_log='OFF';
set @save_storage_engine= @@session.storage_engine;
set storage_engine= MEMORY;
alter table mysql.slow_log engine=ndb;
-ERROR HY000: This storage engine cannot be used for log tables"
+ERROR HY000: This storage engine cannot be used for log tables
alter table mysql.slow_log engine=innodb;
-ERROR HY000: This storage engine cannot be used for log tables"
+ERROR HY000: This storage engine cannot be used for log tables
alter table mysql.slow_log engine=archive;
-ERROR HY000: This storage engine cannot be used for log tables"
+ERROR HY000: This storage engine cannot be used for log tables
alter table mysql.slow_log engine=blackhole;
-ERROR HY000: This storage engine cannot be used for log tables"
+ERROR HY000: This storage engine cannot be used for log tables
set storage_engine= @save_storage_engine;
drop table mysql.slow_log;
drop table mysql.general_log;
diff --git a/mysql-test/r/plugin.result b/mysql-test/r/plugin.result
index ec9d804d6da..98be0326ef0 100644
--- a/mysql-test/r/plugin.result
+++ b/mysql-test/r/plugin.result
@@ -75,9 +75,9 @@ SET SQL_MODE='IGNORE_BAD_TABLE_OPTIONS';
#illegal value fixed
CREATE TABLE t1 (a int) ENGINE=example ULL=10000000000000000000 one_or_two='ttt' YESNO=SSS;
Warnings:
-Warning 1652 Incorrect value '10000000000000000000' for option 'ULL'
-Warning 1652 Incorrect value 'ttt' for option 'one_or_two'
-Warning 1652 Incorrect value 'SSS' for option 'YESNO'
+Warning 1653 Incorrect value '10000000000000000000' for option 'ULL'
+Warning 1653 Incorrect value 'ttt' for option 'one_or_two'
+Warning 1653 Incorrect value 'SSS' for option 'YESNO'
show create table t1;
Table Create Table
t1 CREATE TABLE `t1` (
diff --git a/mysql-test/r/sp-threads.result b/mysql-test/r/sp-threads.result
index 953830ecc87..d974cfb9605 100644
--- a/mysql-test/r/sp-threads.result
+++ b/mysql-test/r/sp-threads.result
@@ -35,7 +35,7 @@ call bug9486();
show processlist;
Id User Host db Command Time State Info
# root localhost test Sleep # NULL
-# root localhost test Query # Locked update t1, t2 set val= 1 where id1=id2
+# root localhost test Query # Table lock update t1, t2 set val= 1 where id1=id2
# root localhost test Query # NULL show processlist
# root localhost test Sleep # NULL
unlock tables;
diff --git a/mysql-test/r/subselect.result b/mysql-test/r/subselect.result
index de2aeedb91f..73d14acd532 100644
--- a/mysql-test/r/subselect.result
+++ b/mysql-test/r/subselect.result
@@ -4953,6 +4953,17 @@ sum(a) sub
1 3
deallocate prepare stmt1;
drop table t1,t2;
+#
+# Bug LP#693935/#58727: Assertion failure with
+# a single row subquery returning more than one row
+#
+create table t1 (a char(1) charset utf8);
+insert into t1 values ('a'), ('b');
+create table t2 (a binary(1));
+insert into t2 values ('x'), ('y');
+select * from t2 where a=(select a from t1) and a='x';
+ERROR 21000: Subquery returns more than 1 row
+drop table t1,t2;
End of 5.1 tests
#
# No BUG#, a case brought from 5.2's innodb_mysql_lock.test
diff --git a/mysql-test/r/subselect_no_mat.result b/mysql-test/r/subselect_no_mat.result
index 7aec870308c..486bb03373e 100644
--- a/mysql-test/r/subselect_no_mat.result
+++ b/mysql-test/r/subselect_no_mat.result
@@ -4956,6 +4956,17 @@ sum(a) sub
1 3
deallocate prepare stmt1;
drop table t1,t2;
+#
+# Bug LP#693935/#58727: Assertion failure with
+# a single row subquery returning more than one row
+#
+create table t1 (a char(1) charset utf8);
+insert into t1 values ('a'), ('b');
+create table t2 (a binary(1));
+insert into t2 values ('x'), ('y');
+select * from t2 where a=(select a from t1) and a='x';
+ERROR 21000: Subquery returns more than 1 row
+drop table t1,t2;
End of 5.1 tests
#
# No BUG#, a case brought from 5.2's innodb_mysql_lock.test
diff --git a/mysql-test/r/subselect_no_opts.result b/mysql-test/r/subselect_no_opts.result
index 3272b95b81f..07412a0c72c 100644
--- a/mysql-test/r/subselect_no_opts.result
+++ b/mysql-test/r/subselect_no_opts.result
@@ -4953,6 +4953,17 @@ sum(a) sub
1 3
deallocate prepare stmt1;
drop table t1,t2;
+#
+# Bug LP#693935/#58727: Assertion failure with
+# a single row subquery returning more than one row
+#
+create table t1 (a char(1) charset utf8);
+insert into t1 values ('a'), ('b');
+create table t2 (a binary(1));
+insert into t2 values ('x'), ('y');
+select * from t2 where a=(select a from t1) and a='x';
+ERROR 21000: Subquery returns more than 1 row
+drop table t1,t2;
End of 5.1 tests
#
# No BUG#, a case brought from 5.2's innodb_mysql_lock.test
diff --git a/mysql-test/r/subselect_no_semijoin.result b/mysql-test/r/subselect_no_semijoin.result
index f7ff32ab6d6..737fd5f2eb5 100644
--- a/mysql-test/r/subselect_no_semijoin.result
+++ b/mysql-test/r/subselect_no_semijoin.result
@@ -4953,6 +4953,17 @@ sum(a) sub
1 3
deallocate prepare stmt1;
drop table t1,t2;
+#
+# Bug LP#693935/#58727: Assertion failure with
+# a single row subquery returning more than one row
+#
+create table t1 (a char(1) charset utf8);
+insert into t1 values ('a'), ('b');
+create table t2 (a binary(1));
+insert into t2 values ('x'), ('y');
+select * from t2 where a=(select a from t1) and a='x';
+ERROR 21000: Subquery returns more than 1 row
+drop table t1,t2;
End of 5.1 tests
#
# No BUG#, a case brought from 5.2's innodb_mysql_lock.test
diff --git a/mysql-test/r/table_elim.result b/mysql-test/r/table_elim.result
index e635f745ed3..06135e28efb 100644
--- a/mysql-test/r/table_elim.result
+++ b/mysql-test/r/table_elim.result
@@ -535,3 +535,34 @@ HAVING
field4 != 6;
field1 field2 field3 field4 field5 field6
drop table t0,t1,t2,t3,t4,t5,t6;
+#
+# BUG#675118: Elimination of a table results in an invalid execution plan
+#
+CREATE TABLE t1 (f1 int(11), PRIMARY KEY (f1)) ;
+CREATE TABLE t2 (f4 varchar(1024), KEY (f4)) ;
+Warnings:
+Warning 1071 Specified key was too long; max key length is 1000 bytes
+INSERT IGNORE INTO t2 VALUES ('xcddwntkbxyorzdv'),
+('cnxxcddwntkbxyor'),('r'),('r'), ('did'),('I'),('when'),
+('hczkfqjeggivdvac'),('e'),('okay'),('up');
+CREATE TABLE t3 (f4 varchar(1024), f1 int(11), f2 int(11)) ;
+INSERT IGNORE INTO t3 VALUES ('f','4','0'),('n','5','-996540416');
+CREATE TABLE t4 (f1 int(11), f3 varchar(10)) ;
+INSERT IGNORE INTO t4 VALUES ('8','n'),('9','nwzcerzsgx'),('10','c');
+CREATE TABLE t5 (f5 int(11), KEY (f5)) ;
+EXPLAIN
+SELECT t3.f2
+FROM t2
+LEFT JOIN t3
+LEFT JOIN t4
+LEFT JOIN t1 ON t4.f1 = t1.f1
+JOIN t5 ON t4.f3 ON t3.f1 = t5.f5 ON t2.f4 = t3.f4
+WHERE t3.f2 ;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t3 ALL NULL NULL NULL NULL 2 Using where
+1 SIMPLE t5 ref f5 f5 5 test.t3.f1 2 Using where; Using index
+1 SIMPLE t4 ALL NULL NULL NULL NULL 3 Using where
+1 SIMPLE t2 ALL f4 NULL NULL NULL 11 Using where; Using join buffer (flat, BNL join)
+# ^^ The above must not produce a QEP of t3,t5,t2,t4
+# as that violates the "no interleaving of outer join nests" rule.
+DROP TABLE t1,t2,t3,t4,t5;
diff --git a/mysql-test/r/table_options.result b/mysql-test/r/table_options.result
index ed6fe4fb3de..ddb7bd13c03 100644
--- a/mysql-test/r/table_options.result
+++ b/mysql-test/r/table_options.result
@@ -3,9 +3,9 @@ SET @OLD_SQL_MODE=@@SQL_MODE;
SET SQL_MODE='IGNORE_BAD_TABLE_OPTIONS';
create table t1 (a int fkey=vvv, key akey (a) dff=vvv) tkey1='1v1';
Warnings:
-Warning 1651 Unknown option 'fkey'
-Warning 1651 Unknown option 'dff'
-Warning 1651 Unknown option 'tkey1'
+Warning 1652 Unknown option 'fkey'
+Warning 1652 Unknown option 'dff'
+Warning 1652 Unknown option 'tkey1'
show create table t1;
Table Create Table
t1 CREATE TABLE `t1` (
@@ -16,10 +16,10 @@ drop table t1;
#reassiginig options in the same line
create table t1 (a int fkey=vvv, key akey (a) dff=vvv) tkey1=1v1 TKEY1=DEFAULT tkey1=1v2 tkey2=2v1;
Warnings:
-Warning 1651 Unknown option 'fkey'
-Warning 1651 Unknown option 'dff'
-Warning 1651 Unknown option 'tkey1'
-Warning 1651 Unknown option 'tkey2'
+Warning 1652 Unknown option 'fkey'
+Warning 1652 Unknown option 'dff'
+Warning 1652 Unknown option 'tkey1'
+Warning 1652 Unknown option 'tkey2'
show create table t1;
Table Create Table
t1 CREATE TABLE `t1` (
@@ -29,7 +29,7 @@ t1 CREATE TABLE `t1` (
#add option
alter table t1 tkey4=4v1;
Warnings:
-Warning 1651 Unknown option 'tkey4'
+Warning 1652 Unknown option 'tkey4'
show create table t1;
Table Create Table
t1 CREATE TABLE `t1` (
@@ -39,8 +39,8 @@ t1 CREATE TABLE `t1` (
#remove options
alter table t1 tkey3=DEFAULT tkey4=DEFAULT;
Warnings:
-Warning 1651 Unknown option 'tkey3'
-Warning 1651 Unknown option 'tkey4'
+Warning 1652 Unknown option 'tkey3'
+Warning 1652 Unknown option 'tkey4'
show create table t1;
Table Create Table
t1 CREATE TABLE `t1` (
@@ -50,11 +50,11 @@ t1 CREATE TABLE `t1` (
drop table t1;
create table t1 (a int fkey1=v1, key akey (a) kkey1=v1) tkey1=1v1 tkey1=1v2 TKEY1=DEFAULT tkey2=2v1 tkey3=3v1;
Warnings:
-Warning 1651 Unknown option 'fkey1'
-Warning 1651 Unknown option 'kkey1'
-Warning 1651 Unknown option 'TKEY1'
-Warning 1651 Unknown option 'tkey2'
-Warning 1651 Unknown option 'tkey3'
+Warning 1652 Unknown option 'fkey1'
+Warning 1652 Unknown option 'kkey1'
+Warning 1652 Unknown option 'TKEY1'
+Warning 1652 Unknown option 'tkey2'
+Warning 1652 Unknown option 'tkey3'
show create table t1;
Table Create Table
t1 CREATE TABLE `t1` (
@@ -64,7 +64,7 @@ t1 CREATE TABLE `t1` (
#change field with option with the same value
alter table t1 change a a int `FKEY1`='v1';
Warnings:
-Warning 1651 Unknown option 'FKEY1'
+Warning 1652 Unknown option 'FKEY1'
show create table t1;
Table Create Table
t1 CREATE TABLE `t1` (
@@ -74,7 +74,7 @@ t1 CREATE TABLE `t1` (
#change field with option with a different value
alter table t1 change a a int fkey1=v2;
Warnings:
-Warning 1651 Unknown option 'fkey1'
+Warning 1652 Unknown option 'fkey1'
show create table t1;
Table Create Table
t1 CREATE TABLE `t1` (
@@ -93,7 +93,7 @@ t1 CREATE TABLE `t1` (
#new key with options
alter table t1 add key bkey (b) kkey2=v1;
Warnings:
-Warning 1651 Unknown option 'kkey2'
+Warning 1652 Unknown option 'kkey2'
show create table t1;
Table Create Table
t1 CREATE TABLE `t1` (
@@ -105,8 +105,8 @@ t1 CREATE TABLE `t1` (
#new column with options
alter table t1 add column c int fkey1=v1 fkey2=v2;
Warnings:
-Warning 1651 Unknown option 'fkey1'
-Warning 1651 Unknown option 'fkey2'
+Warning 1652 Unknown option 'fkey1'
+Warning 1652 Unknown option 'fkey2'
show create table t1;
Table Create Table
t1 CREATE TABLE `t1` (
@@ -141,7 +141,7 @@ t1 CREATE TABLE `t1` (
#add column with options after delete
alter table t1 add column b int fkey2=v1;
Warnings:
-Warning 1651 Unknown option 'fkey2'
+Warning 1652 Unknown option 'fkey2'
show create table t1;
Table Create Table
t1 CREATE TABLE `t1` (
@@ -154,7 +154,7 @@ t1 CREATE TABLE `t1` (
#add key
alter table t1 add key bkey (b) kkey2=v2;
Warnings:
-Warning 1651 Unknown option 'kkey2'
+Warning 1652 Unknown option 'kkey2'
show create table t1;
Table Create Table
t1 CREATE TABLE `t1` (
@@ -168,7 +168,7 @@ t1 CREATE TABLE `t1` (
drop table t1;
create table t1 (a int) tkey1=100;
Warnings:
-Warning 1651 Unknown option 'tkey1'
+Warning 1652 Unknown option 'tkey1'
show create table t1;
Table Create Table
t1 CREATE TABLE `t1` (
diff --git a/mysql-test/suite/binlog/t/binlog_stm_row.test b/mysql-test/suite/binlog/t/binlog_stm_row.test
index 29b0a69330d..47d9cbbbfb6 100644
--- a/mysql-test/suite/binlog/t/binlog_stm_row.test
+++ b/mysql-test/suite/binlog/t/binlog_stm_row.test
@@ -60,7 +60,7 @@ let $wait_condition=
--echo # con1
let $wait_condition=
SELECT COUNT(*) = 1 FROM information_schema.processlist WHERE
- state = "Locked" and info = "INSERT INTO t2 VALUES (3)";
+ state = "Table Lock" and info = "INSERT INTO t2 VALUES (3)";
--source include/wait_condition.inc
SELECT RELEASE_LOCK('Bug#34306');
--connection con2
diff --git a/mysql-test/suite/federated/federated_debug.test b/mysql-test/suite/federated/federated_debug.test
index 4152d2975b3..d63c1007088 100644
--- a/mysql-test/suite/federated/federated_debug.test
+++ b/mysql-test/suite/federated/federated_debug.test
@@ -1,4 +1,5 @@
--source include/have_debug.inc
+--source include/long_test.inc
--source federated.inc
--echo #
diff --git a/mysql-test/suite/funcs_1/datadict/processlist_val.inc b/mysql-test/suite/funcs_1/datadict/processlist_val.inc
index b1c1130cbdf..e06c5f081f5 100644
--- a/mysql-test/suite/funcs_1/datadict/processlist_val.inc
+++ b/mysql-test/suite/funcs_1/datadict/processlist_val.inc
@@ -368,13 +368,13 @@ echo
;
connection default;
echo
-# Poll till INFO is no more NULL and State = 'Locked'.
+# Poll till INFO is no more NULL and State = "Table Lock".
;
let $wait_condition= SELECT COUNT(*) FROM INFORMATION_SCHEMA.PROCESSLIST
- WHERE INFO IS NOT NULL AND STATE = 'Locked';
+ WHERE INFO IS NOT NULL AND STATE = "Table Lock";
--source include/wait_condition.inc
#
-# Expect to see the state 'Locked' for the third connection because the SELECT
+# Expect to see the state "Table Lock" for the third connection because the SELECT
# collides with the WRITE TABLE LOCK.
--replace_column 1 <ID> 3 <HOST_NAME> 6 <TIME> 9 <TIME_MS>
SELECT * FROM INFORMATION_SCHEMA.PROCESSLIST;
@@ -423,10 +423,10 @@ echo
;
connection default;
echo
-# Poll till INFO is no more NULL and State = 'Locked'.
+# Poll till INFO is no more NULL and State = "Table Lock".
;
let $wait_condition= SELECT COUNT(*) FROM INFORMATION_SCHEMA.PROCESSLIST
- WHERE INFO IS NOT NULL AND STATE = 'Locked';
+ WHERE INFO IS NOT NULL AND STATE = "Table Lock";
--source include/wait_condition.inc
echo
# Expect result:
diff --git a/mysql-test/suite/handler/aria.result b/mysql-test/suite/handler/aria.result
new file mode 100644
index 00000000000..58ecdaa1f09
--- /dev/null
+++ b/mysql-test/suite/handler/aria.result
@@ -0,0 +1,801 @@
+SET SESSION STORAGE_ENGINE = Aria;
+drop table if exists t1,t3,t4,t5;
+create table t1 (a int, b char(10), key a (a), key b (a,b));
+insert into t1 values
+(17,"ddd"),(18,"eee"),(19,"fff"),(19,"yyy"),
+(14,"aaa"),(16,"ccc"),(16,"xxx"),
+(20,"ggg"),(21,"hhh"),(22,"iii");
+handler t1 open as t2;
+handler t2 read b first;
+a b
+14 aaa
+handler t2 read b next;
+a b
+16 ccc
+handler t2 read b next;
+a b
+16 xxx
+handler t2 read b prev;
+a b
+16 ccc
+handler t2 read b last;
+a b
+22 iii
+handler t2 read b prev;
+a b
+21 hhh
+handler t2 read b prev;
+a b
+20 ggg
+handler t2 read b first;
+a b
+14 aaa
+handler t2 read b prev;
+a b
+handler t2 read b last;
+a b
+22 iii
+handler t2 read b prev;
+a b
+21 hhh
+handler t2 read b next;
+a b
+22 iii
+handler t2 read b next;
+a b
+handler t2 read a=(15);
+a b
+handler t2 read a=(21);
+a b
+21 hhh
+handler t2 read a=(19,"fff");
+ERROR 42000: Too many key parts specified; max 1 parts allowed
+handler t2 read b=(19,"fff");
+a b
+19 fff
+handler t2 read b=(19,"yyy");
+a b
+19 yyy
+handler t2 read b=(19);
+a b
+19 fff
+handler t1 read a last;
+ERROR 42S02: Unknown table 't1' in HANDLER
+handler t2 read a=(11);
+a b
+handler t2 read a>=(11);
+a b
+14 aaa
+handler t2 read b=(18);
+a b
+18 eee
+handler t2 read b>=(18);
+a b
+18 eee
+handler t2 read b>(18);
+a b
+19 fff
+handler t2 read b<=(18);
+a b
+18 eee
+handler t2 read b<(18);
+a b
+17 ddd
+handler t2 read a=(15);
+a b
+handler t2 read a>=(15) limit 2;
+a b
+16 ccc
+16 xxx
+handler t2 read a>(15) limit 2;
+a b
+16 ccc
+16 xxx
+handler t2 read a<=(15);
+a b
+14 aaa
+handler t2 read a<(15);
+a b
+14 aaa
+handler t2 read a=(54);
+a b
+handler t2 read a>=(54);
+a b
+handler t2 read a>(54);
+a b
+handler t2 read a<=(54);
+a b
+22 iii
+handler t2 read a<(54);
+a b
+22 iii
+handler t2 read a=(1);
+a b
+handler t2 read a>=(1);
+a b
+14 aaa
+handler t2 read a>(1);
+a b
+14 aaa
+handler t2 read a<=(1);
+a b
+handler t2 read a<(1);
+a b
+handler t2 read b first limit 5;
+a b
+14 aaa
+16 ccc
+16 xxx
+17 ddd
+18 eee
+handler t2 read b next limit 3;
+a b
+19 fff
+19 yyy
+20 ggg
+handler t2 read b prev limit 10;
+a b
+19 yyy
+19 fff
+18 eee
+17 ddd
+16 xxx
+16 ccc
+14 aaa
+handler t2 read b>=(16) limit 4;
+a b
+16 ccc
+16 xxx
+17 ddd
+18 eee
+handler t2 read b>=(16) limit 2,2;
+a b
+17 ddd
+18 eee
+select * from t1 where a>=16 order by a,b limit 2,2;
+a b
+17 ddd
+18 eee
+handler t2 read a last limit 3;
+a b
+22 iii
+21 hhh
+20 ggg
+handler t2 read b=(16) limit 1,3;
+a b
+16 xxx
+handler t2 read b=(19);
+a b
+19 fff
+handler t2 read b=(19) where b="yyy";
+a b
+19 yyy
+handler t2 read first;
+a b
+17 ddd
+handler t2 read next;
+a b
+18 eee
+handler t2 read next;
+a b
+19 fff
+handler t2 close;
+handler t1 open;
+handler t1 read b next;
+a b
+14 aaa
+handler t1 read b next;
+a b
+16 ccc
+handler t1 close;
+handler t1 open;
+handler t1 read a prev;
+a b
+22 iii
+handler t1 read a prev;
+a b
+21 hhh
+handler t1 close;
+handler t1 open as t2;
+handler t2 read first;
+a b
+17 ddd
+alter table t1 engine = Aria;
+handler t2 read first;
+ERROR 42S02: Unknown table 't2' in HANDLER
+handler t1 open;
+handler t1 read a=(16) limit 1,3;
+a b
+16 xxx
+flush tables;
+handler t1 read a=(16) limit 1,3;
+a b
+16 xxx
+handler t1 close;
+handler t1 open;
+prepare stmt from 'handler t1 read a=(?) limit ?,?';
+set @a=16,@b=1,@c=100;
+execute stmt using @a,@b,@c;
+a b
+16 xxx
+set @a=16,@b=2,@c=1;
+execute stmt using @a,@b,@c;
+a b
+set @a=16,@b=0,@c=2;
+execute stmt using @a,@b,@c;
+a b
+16 ccc
+16 xxx
+deallocate prepare stmt;
+prepare stmt from 'handler t1 read a next limit ?';
+handler t1 read a>=(11);
+a b
+14 aaa
+set @a=3;
+execute stmt using @a;
+a b
+16 ccc
+16 xxx
+17 ddd
+execute stmt using @a;
+a b
+18 eee
+19 fff
+19 yyy
+execute stmt using @a;
+a b
+20 ggg
+21 hhh
+22 iii
+deallocate prepare stmt;
+prepare stmt from 'handler t1 read b prev limit ?';
+execute stmt using @a;
+a b
+22 iii
+21 hhh
+20 ggg
+execute stmt using @a;
+a b
+19 yyy
+19 fff
+18 eee
+execute stmt using @a;
+a b
+17 ddd
+16 xxx
+16 ccc
+execute stmt using @a;
+a b
+14 aaa
+deallocate prepare stmt;
+prepare stmt from 'handler t1 read b=(?,?)';
+set @a=14, @b='aaa';
+execute stmt using @a,@b;
+a b
+14 aaa
+set @a=14, @b='not found';
+execute stmt using @a,@b;
+a b
+deallocate prepare stmt;
+prepare stmt from 'handler t1 read b=(1+?) limit 10';
+set @a=15;
+execute stmt using @a;
+a b
+16 ccc
+16 xxx
+execute stmt using @a;
+a b
+16 ccc
+16 xxx
+deallocate prepare stmt;
+prepare stmt from 'handler t1 read a>=(?) where a < ? limit 5';
+set @a=15, @b=20;
+execute stmt using @a,@b;
+a b
+16 ccc
+16 xxx
+17 ddd
+18 eee
+19 fff
+execute stmt using @a,@b;
+a b
+16 ccc
+16 xxx
+17 ddd
+18 eee
+19 fff
+deallocate prepare stmt;
+prepare stmt from 'handler t1 read a=(?)';
+set @a=16;
+execute stmt using @a;
+a b
+16 ccc
+alter table t1 add c int;
+execute stmt using @a;
+ERROR 42S02: Unknown table 't1' in HANDLER
+deallocate prepare stmt;
+handler t1 close;
+ERROR 42S02: Unknown table 't1' in HANDLER
+handler t1 open;
+prepare stmt from 'handler t1 read a=(?)';
+flush tables;
+set @a=16;
+execute stmt using @a;
+ERROR HY000: Prepared statement needs to be re-prepared
+deallocate prepare stmt;
+handler t1 close;
+handler t1 open as t2;
+drop table t1;
+create table t1 (a int not null);
+insert into t1 values (17);
+handler t2 read first;
+ERROR 42S02: Unknown table 't2' in HANDLER
+handler t1 open as t2;
+alter table t1 engine=CSV;
+handler t2 read first;
+ERROR 42S02: Unknown table 't2' in HANDLER
+drop table t1;
+create table t1 (a int);
+insert into t1 values (1),(2),(3),(4),(5),(6);
+delete from t1 limit 2;
+handler t1 open;
+handler t1 read first;
+a
+3
+handler t1 read first limit 1,1;
+a
+4
+handler t1 read first limit 2,2;
+a
+5
+6
+delete from t1 limit 3;
+handler t1 read first;
+a
+6
+drop table t1;
+create table t1(a int, index (a));
+insert into t1 values (1), (2), (3);
+handler t1 open;
+handler t1 read a=(W);
+ERROR 42S22: Unknown column 'W' in 'field list'
+handler t1 read a=(a);
+ERROR HY000: Incorrect arguments to HANDLER ... READ
+drop table t1;
+create table t1 (a char(5));
+insert into t1 values ("Ok");
+handler t1 open as t;
+handler t read first;
+a
+Ok
+use mysql;
+handler t read first;
+a
+Ok
+handler t close;
+handler test.t1 open as t;
+handler t read first;
+a
+Ok
+handler t close;
+use test;
+drop table t1;
+create table t1 ( a int, b int, INDEX a (a) );
+insert into t1 values (1,2), (2,1);
+handler t1 open;
+handler t1 read a=(1) where b=2;
+a b
+1 2
+handler t1 read a=(1) where b=3;
+a b
+handler t1 read a=(1) where b=1;
+a b
+handler t1 close;
+drop table t1;
+create table t1 (c1 char(20));
+insert into t1 values ("t1");
+handler t1 open as h1;
+handler h1 read first limit 9;
+c1
+t1
+create table t2 (c1 char(20));
+insert into t2 values ("t2");
+handler t2 open as h2;
+handler h2 read first limit 9;
+c1
+t2
+create table t3 (c1 char(20));
+insert into t3 values ("t3");
+handler t3 open as h3;
+handler h3 read first limit 9;
+c1
+t3
+create table t4 (c1 char(20));
+insert into t4 values ("t4");
+handler t4 open as h4;
+handler h4 read first limit 9;
+c1
+t4
+create table t5 (c1 char(20));
+insert into t5 values ("t5");
+handler t5 open as h5;
+handler h5 read first limit 9;
+c1
+t5
+alter table t1 engine=MyISAM;
+handler h1 read first limit 9;
+ERROR 42S02: Unknown table 'h1' in HANDLER
+handler h2 read first limit 9;
+c1
+t2
+handler h3 read first limit 9;
+c1
+t3
+handler h4 read first limit 9;
+c1
+t4
+handler h5 read first limit 9;
+c1
+t5
+alter table t5 engine=MyISAM;
+handler h1 read first limit 9;
+ERROR 42S02: Unknown table 'h1' in HANDLER
+handler h2 read first limit 9;
+c1
+t2
+handler h3 read first limit 9;
+c1
+t3
+handler h4 read first limit 9;
+c1
+t4
+handler h5 read first limit 9;
+ERROR 42S02: Unknown table 'h5' in HANDLER
+alter table t3 engine=MyISAM;
+handler h1 read first limit 9;
+ERROR 42S02: Unknown table 'h1' in HANDLER
+handler h2 read first limit 9;
+c1
+t2
+handler h3 read first limit 9;
+ERROR 42S02: Unknown table 'h3' in HANDLER
+handler h4 read first limit 9;
+c1
+t4
+handler h5 read first limit 9;
+ERROR 42S02: Unknown table 'h5' in HANDLER
+handler h2 close;
+handler h4 close;
+handler t1 open as h1_1;
+handler t1 open as h1_2;
+handler t1 open as h1_3;
+handler h1_1 read first limit 9;
+c1
+t1
+handler h1_2 read first limit 9;
+c1
+t1
+handler h1_3 read first limit 9;
+c1
+t1
+alter table t1 engine=Aria;
+handler h1_1 read first limit 9;
+ERROR 42S02: Unknown table 'h1_1' in HANDLER
+handler h1_2 read first limit 9;
+ERROR 42S02: Unknown table 'h1_2' in HANDLER
+handler h1_3 read first limit 9;
+ERROR 42S02: Unknown table 'h1_3' in HANDLER
+drop table t1;
+drop table t2;
+drop table t3;
+drop table t4;
+drop table t5;
+create table t1 (c1 int);
+insert into t1 values (1);
+handler t1 open;
+handler t1 read first;
+c1
+1
+send the below to another connection, do not wait for the result
+optimize table t1;
+proceed with the normal connection
+handler t1 read next;
+c1
+1
+handler t1 close;
+read the result from the other connection
+Table Op Msg_type Msg_text
+test.t1 optimize status OK
+proceed with the normal connection
+drop table t1;
+CREATE TABLE t1 ( no1 smallint(5) NOT NULL default '0', no2 int(10) NOT NULL default '0', PRIMARY KEY (no1,no2));
+INSERT INTO t1 VALUES (1,274),(1,275),(2,6),(2,8),(4,1),(4,2);
+HANDLER t1 OPEN;
+HANDLER t1 READ `primary` = (1, 1000);
+no1 no2
+HANDLER t1 READ `primary` PREV;
+no1 no2
+1 275
+HANDLER t1 READ `primary` = (1, 1000);
+no1 no2
+HANDLER t1 READ `primary` NEXT;
+no1 no2
+2 6
+DROP TABLE t1;
+create table t1 (c1 int);
+insert into t1 values (14397);
+flush tables with read lock;
+drop table t1;
+ERROR HY000: Can't execute the query because you have a conflicting read lock
+send the below to another connection, do not wait for the result
+drop table t1;
+proceed with the normal connection
+select * from t1;
+c1
+14397
+unlock tables;
+read the result from the other connection
+proceed with the normal connection
+select * from t1;
+ERROR 42S02: Table 'test.t1' doesn't exist
+drop table if exists t1;
+Warnings:
+Note 1051 Unknown table 't1'
+create table t1 (a int not null) ENGINE=CSV;
+--> client 2
+handler t1 open;
+ERROR HY000: Table storage engine for 't1' doesn't have this option
+--> client 1
+drop table t1;
+create table t1 (a int);
+handler t1 open as t1_alias;
+handler t1_alias read a next;
+ERROR 42000: Key 'a' doesn't exist in table 't1_alias'
+handler t1_alias READ a next where inexistent > 0;
+ERROR 42S22: Unknown column 'inexistent' in 'field list'
+handler t1_alias read a next;
+ERROR 42000: Key 'a' doesn't exist in table 't1_alias'
+handler t1_alias READ a next where inexistent > 0;
+ERROR 42S22: Unknown column 'inexistent' in 'field list'
+handler t1_alias close;
+drop table t1;
+create temporary table t1 (a int, b char(1), key a (a), key b (a,b));
+insert into t1 values (0,"a"),(1,"b"),(2,"c"),(3,"d"),(4,"e"),
+(5,"f"),(6,"g"),(7,"h"),(8,"i"),(9,"j"),(9,'k');
+select a,b from t1;
+a b
+0 a
+1 b
+2 c
+3 d
+4 e
+5 f
+6 g
+7 h
+8 i
+9 j
+9 k
+handler t1 open as a1;
+handler a1 read a=(1);
+a b
+1 b
+handler a1 read a next;
+a b
+2 c
+handler a1 read a next;
+a b
+3 d
+select a,b from t1;
+ERROR HY000: Can't reopen table: 'a1'
+handler a1 read a prev;
+a b
+2 c
+handler a1 read a prev;
+a b
+1 b
+handler a1 read a=(6) where b="g";
+a b
+6 g
+handler a1 close;
+select a,b from t1;
+a b
+0 a
+1 b
+2 c
+3 d
+4 e
+5 f
+6 g
+7 h
+8 i
+9 j
+9 k
+handler t1 open as a2;
+handler a2 read b=(9);
+a b
+9 j
+handler a2 read b next;
+a b
+9 k
+handler a2 read b prev limit 2;
+a b
+9 j
+8 i
+handler a2 read b last;
+a b
+9 k
+handler a2 read b prev;
+a b
+9 j
+handler a2 close;
+drop table t1;
+create table t1 (a int);
+create temporary table t2 (a int, key (a));
+handler t1 open as a1;
+handler t2 open as a2;
+handler a2 read a first;
+a
+drop table t1, t2;
+handler a2 read a next;
+ERROR 42S02: Unknown table 'a2' in HANDLER
+handler a1 close;
+ERROR 42S02: Unknown table 'a1' in HANDLER
+create table t1 (a int, key (a));
+create table t2 like t1;
+handler t1 open as a1;
+handler t2 open as a2;
+handler a1 read a first;
+a
+handler a2 read a first;
+a
+alter table t1 add b int;
+handler a1 close;
+ERROR 42S02: Unknown table 'a1' in HANDLER
+handler a2 close;
+drop table t1, t2;
+create table t1 (a int, key (a));
+handler t1 open as a1;
+handler a1 read a first;
+a
+rename table t1 to t2;
+handler a1 read a first;
+ERROR 42S02: Unknown table 'a1' in HANDLER
+drop table t2;
+create table t1 (a int, key (a));
+create table t2 like t1;
+handler t1 open as a1;
+handler t2 open as a2;
+handler a1 read a first;
+a
+handler a2 read a first;
+a
+optimize table t1;
+Table Op Msg_type Msg_text
+test.t1 optimize status Table is already up to date
+handler a1 close;
+ERROR 42S02: Unknown table 'a1' in HANDLER
+handler a2 close;
+drop table t1, t2;
+#
+# BUG#51877 - HANDLER interface causes invalid memory read
+#
+CREATE TABLE t1(a INT, KEY (a));
+HANDLER t1 OPEN;
+HANDLER t1 READ a FIRST;
+a
+INSERT INTO t1 VALUES(1);
+HANDLER t1 READ a NEXT;
+a
+1
+HANDLER t1 CLOSE;
+DROP TABLE t1;
+#
+# BUG #46456: HANDLER OPEN + TRUNCATE + DROP (temporary) TABLE, crash
+#
+CREATE TABLE t1 AS SELECT 1 AS f1;
+HANDLER t1 OPEN;
+TRUNCATE t1;
+HANDLER t1 READ FIRST;
+ERROR 42S02: Unknown table 't1' in HANDLER
+DROP TABLE t1;
+CREATE TEMPORARY TABLE t1 AS SELECT 1 AS f1;
+HANDLER t1 OPEN;
+TRUNCATE t1;
+HANDLER t1 READ FIRST;
+ERROR 42S02: Unknown table 't1' in HANDLER
+DROP TABLE t1;
+#
+# Bug #54007: assert in ha_myisam::index_next , HANDLER
+#
+CREATE TABLE t1(a INT, b INT, PRIMARY KEY(a), KEY b(b), KEY ab(a, b));
+HANDLER t1 OPEN;
+HANDLER t1 READ FIRST;
+a b
+HANDLER t1 READ `PRIMARY` NEXT;
+a b
+HANDLER t1 READ ab NEXT;
+a b
+HANDLER t1 READ b NEXT;
+a b
+HANDLER t1 READ NEXT;
+a b
+HANDLER t1 CLOSE;
+INSERT INTO t1 VALUES (2, 20), (1, 10), (4, 40), (3, 30);
+HANDLER t1 OPEN;
+HANDLER t1 READ FIRST;
+a b
+2 20
+HANDLER t1 READ NEXT;
+a b
+1 10
+HANDLER t1 READ `PRIMARY` NEXT;
+a b
+1 10
+HANDLER t1 READ `PRIMARY` NEXT;
+a b
+2 20
+HANDLER t1 READ ab NEXT;
+a b
+1 10
+HANDLER t1 READ ab NEXT;
+a b
+2 20
+HANDLER t1 READ b NEXT;
+a b
+1 10
+HANDLER t1 READ b NEXT;
+a b
+2 20
+HANDLER t1 READ b NEXT;
+a b
+3 30
+HANDLER t1 READ b NEXT;
+a b
+4 40
+HANDLER t1 READ b NEXT;
+a b
+HANDLER t1 READ NEXT;
+a b
+2 20
+HANDLER t1 READ NEXT;
+a b
+1 10
+HANDLER t1 READ NEXT;
+a b
+4 40
+HANDLER t1 CLOSE;
+HANDLER t1 OPEN;
+HANDLER t1 READ FIRST;
+a b
+2 20
+HANDLER t1 READ `PRIMARY` PREV;
+a b
+4 40
+HANDLER t1 READ `PRIMARY` PREV;
+a b
+3 30
+HANDLER t1 READ b PREV;
+a b
+4 40
+HANDLER t1 READ b PREV;
+a b
+3 30
+HANDLER t1 CLOSE;
+HANDLER t1 OPEN;
+HANDLER t1 READ FIRST;
+a b
+2 20
+HANDLER t1 READ `PRIMARY` PREV LIMIT 3;
+a b
+4 40
+3 30
+2 20
+HANDLER t1 READ b NEXT LIMIT 5;
+a b
+1 10
+2 20
+3 30
+4 40
+HANDLER t1 CLOSE;
+DROP TABLE t1;
+End of 5.1 tests
diff --git a/mysql-test/suite/handler/aria.test b/mysql-test/suite/handler/aria.test
new file mode 100644
index 00000000000..1913d2b791c
--- /dev/null
+++ b/mysql-test/suite/handler/aria.test
@@ -0,0 +1,82 @@
+# t/handler_innodb.test
+#
+# test of HANDLER ...
+#
+# Last update:
+# 2006-07-31 ML test refactored (MySQL 5.1)
+# code of t/handler.test and t/innodb_handler.test united
+# main testing code put into handler.inc
+# rename t/innodb_handler.test to t/handler_innodb.test
+#
+
+--source include/have_maria.inc
+let $engine_type= Aria;
+
+--source init.inc
+--source handler.inc
+
+--echo #
+--echo # BUG #46456: HANDLER OPEN + TRUNCATE + DROP (temporary) TABLE, crash
+--echo #
+CREATE TABLE t1 AS SELECT 1 AS f1;
+HANDLER t1 OPEN;
+TRUNCATE t1;
+--error ER_UNKNOWN_TABLE
+HANDLER t1 READ FIRST;
+DROP TABLE t1;
+
+CREATE TEMPORARY TABLE t1 AS SELECT 1 AS f1;
+HANDLER t1 OPEN;
+TRUNCATE t1;
+--error ER_UNKNOWN_TABLE
+HANDLER t1 READ FIRST;
+DROP TABLE t1;
+
+--echo #
+--echo # Bug #54007: assert in ha_myisam::index_next , HANDLER
+--echo #
+CREATE TABLE t1(a INT, b INT, PRIMARY KEY(a), KEY b(b), KEY ab(a, b));
+
+HANDLER t1 OPEN;
+HANDLER t1 READ FIRST;
+HANDLER t1 READ `PRIMARY` NEXT;
+HANDLER t1 READ ab NEXT;
+HANDLER t1 READ b NEXT;
+HANDLER t1 READ NEXT;
+HANDLER t1 CLOSE;
+
+INSERT INTO t1 VALUES (2, 20), (1, 10), (4, 40), (3, 30);
+HANDLER t1 OPEN;
+HANDLER t1 READ FIRST;
+HANDLER t1 READ NEXT;
+HANDLER t1 READ `PRIMARY` NEXT;
+HANDLER t1 READ `PRIMARY` NEXT;
+HANDLER t1 READ ab NEXT;
+HANDLER t1 READ ab NEXT;
+HANDLER t1 READ b NEXT;
+HANDLER t1 READ b NEXT;
+HANDLER t1 READ b NEXT;
+HANDLER t1 READ b NEXT;
+HANDLER t1 READ b NEXT;
+HANDLER t1 READ NEXT;
+HANDLER t1 READ NEXT;
+HANDLER t1 READ NEXT;
+HANDLER t1 CLOSE;
+
+HANDLER t1 OPEN;
+HANDLER t1 READ FIRST;
+HANDLER t1 READ `PRIMARY` PREV;
+HANDLER t1 READ `PRIMARY` PREV;
+HANDLER t1 READ b PREV;
+HANDLER t1 READ b PREV;
+HANDLER t1 CLOSE;
+
+HANDLER t1 OPEN;
+HANDLER t1 READ FIRST;
+HANDLER t1 READ `PRIMARY` PREV LIMIT 3;
+HANDLER t1 READ b NEXT LIMIT 5;
+HANDLER t1 CLOSE;
+
+DROP TABLE t1;
+
+--echo End of 5.1 tests
diff --git a/mysql-test/include/handler.inc b/mysql-test/suite/handler/handler.inc
index 6e7f53ba9b2..d0ae6164079 100644
--- a/mysql-test/include/handler.inc
+++ b/mysql-test/suite/handler/handler.inc
@@ -1,5 +1,7 @@
-# include/handler.inc
+# handler.inc
#
+# See init.inc for setup of variables for this script
+#
# The variables
# $engine_type -- storage engine to be tested
# $other_engine_type -- storage engine <> $engine_type
@@ -8,49 +10,37 @@
# 2. $other_handler_engine_type must point to an all
# time available storage engine
# 2006-08 MySQL 5.1 MyISAM and MEMORY only
-# have to be set before sourcing this script.
--- source include/not_embedded.inc
#
# test of HANDLER ...
#
# Last update:
# 2006-07-31 ML test refactored (MySQL 5.1)
# code of t/handler.test and t/innodb_handler.test united
-# main testing code put into include/handler.inc
+# main testing code put into handler.inc
#
-eval SET SESSION STORAGE_ENGINE = $engine_type;
-
---disable_warnings
-drop table if exists t1,t3,t4,t5;
---enable_warnings
-
-create table t1 (a int, b char(10), key a(a), key b(a,b));
-insert into t1 values
-(17,"ddd"),(18,"eee"),(19,"fff"),(19,"yyy"),
-(14,"aaa"),(15,"bbb"),(16,"ccc"),(16,"xxx"),
-(20,"ggg"),(21,"hhh"),(22,"iii");
+#
+# Start testing the table created in init.inc
+#
handler t1 open as t2;
--- error 1064
-handler t2 read a=(SELECT 1);
-handler t2 read a first;
-handler t2 read a next;
-handler t2 read a next;
-handler t2 read a prev;
-handler t2 read a last;
-handler t2 read a prev;
-handler t2 read a prev;
-
-handler t2 read a first;
-handler t2 read a prev;
-
-handler t2 read a last;
-handler t2 read a prev;
-handler t2 read a next;
-handler t2 read a next;
+handler t2 read b first;
+handler t2 read b next;
+handler t2 read b next;
+handler t2 read b prev;
+handler t2 read b last;
+handler t2 read b prev;
+handler t2 read b prev;
+
+handler t2 read b first;
+handler t2 read b prev;
+
+handler t2 read b last;
+handler t2 read b prev;
+handler t2 read b next;
+handler t2 read b next;
handler t2 read a=(15);
-handler t2 read a=(16);
+handler t2 read a=(21);
--error 1070
handler t2 read a=(19,"fff");
@@ -65,33 +55,57 @@ handler t1 read a last;
handler t2 read a=(11);
handler t2 read a>=(11);
-handler t2 read a=(18);
-handler t2 read a>=(18);
-handler t2 read a>(18);
-handler t2 read a<=(18);
-handler t2 read a<(18);
-
-handler t2 read a first limit 5;
-handler t2 read a next limit 3;
-handler t2 read a prev limit 10;
+# Search on something we can find
+handler t2 read b=(18);
+handler t2 read b>=(18);
+handler t2 read b>(18);
+handler t2 read b<=(18);
+handler t2 read b<(18);
-handler t2 read a>=(16) limit 4;
-handler t2 read a>=(16) limit 2,2;
+# Search on something we can't find
+--sorted_result
+handler t2 read a=(15);
+--sorted_result
+handler t2 read a>=(15) limit 2;
+--sorted_result
+handler t2 read a>(15) limit 2;
+handler t2 read a<=(15);
+handler t2 read a<(15);
+
+# Search from upper end
+handler t2 read a=(54);
+handler t2 read a>=(54);
+handler t2 read a>(54);
+handler t2 read a<=(54);
+handler t2 read a<(54);
+
+# Search from lower end
+handler t2 read a=(1);
+handler t2 read a>=(1);
+handler t2 read a>(1);
+handler t2 read a<=(1);
+handler t2 read a<(1);
+
+handler t2 read b first limit 5;
+handler t2 read b next limit 3;
+handler t2 read b prev limit 10;
+
+handler t2 read b>=(16) limit 4;
+handler t2 read b>=(16) limit 2,2;
+select * from t1 where a>=16 order by a,b limit 2,2;
handler t2 read a last limit 3;
-
-handler t2 read a=(19);
-handler t2 read a=(19) where b="yyy";
+handler t2 read b=(16) limit 1,3;
+handler t2 read b=(19);
+handler t2 read b=(19) where b="yyy";
handler t2 read first;
handler t2 read next;
handler t2 read next;
---error 1064
-handler t2 read last;
handler t2 close;
handler t1 open;
-handler t1 read a next; # this used to crash as a bug#5373
-handler t1 read a next;
+handler t1 read b next; # this used to crash as a bug#5373
+handler t1 read b next;
handler t1 close;
handler t1 open;
@@ -105,12 +119,85 @@ eval alter table t1 engine = $engine_type;
--error 1109
handler t2 read first;
+handler t1 open;
+handler t1 read a=(16) limit 1,3;
+flush tables;
+handler t1 read a=(16) limit 1,3;
+handler t1 close;
+
+#
+# Test with prepared statements
+#
+
+handler t1 open;
+prepare stmt from 'handler t1 read a=(?) limit ?,?';
+set @a=16,@b=1,@c=100;
+execute stmt using @a,@b,@c;
+set @a=16,@b=2,@c=1;
+execute stmt using @a,@b,@c;
+set @a=16,@b=0,@c=2;
+execute stmt using @a,@b,@c;
+deallocate prepare stmt;
+
+prepare stmt from 'handler t1 read a next limit ?';
+handler t1 read a>=(11);
+set @a=3;
+execute stmt using @a;
+execute stmt using @a;
+execute stmt using @a;
+deallocate prepare stmt;
+
+prepare stmt from 'handler t1 read b prev limit ?';
+execute stmt using @a;
+execute stmt using @a;
+execute stmt using @a;
+execute stmt using @a;
+deallocate prepare stmt;
+
+prepare stmt from 'handler t1 read b=(?,?)';
+set @a=14, @b='aaa';
+execute stmt using @a,@b;
+set @a=14, @b='not found';
+execute stmt using @a,@b;
+deallocate prepare stmt;
+
+prepare stmt from 'handler t1 read b=(1+?) limit 10';
+set @a=15;
+execute stmt using @a;
+execute stmt using @a;
+deallocate prepare stmt;
+
+prepare stmt from 'handler t1 read a>=(?) where a < ? limit 5';
+set @a=15, @b=20;
+execute stmt using @a,@b;
+execute stmt using @a,@b;
+deallocate prepare stmt;
+
+prepare stmt from 'handler t1 read a=(?)';
+set @a=16;
+execute stmt using @a;
+alter table t1 add c int;
+--error 1109
+execute stmt using @a;
+deallocate prepare stmt;
+--error 1109
+handler t1 close;
+
+handler t1 open;
+prepare stmt from 'handler t1 read a=(?)';
+flush tables;
+set @a=16;
+--error ER_NEED_REPREPARE
+execute stmt using @a;
+deallocate prepare stmt;
+handler t1 close;
+
#
# DROP TABLE / ALTER TABLE
#
handler t1 open as t2;
drop table t1;
-create table t1 (a int);
+create table t1 (a int not null);
insert into t1 values (17);
--error 1109
handler t2 read first;
@@ -137,7 +224,7 @@ drop table t1;
#
# Test for #751
#
-create table t1(a int, index(a));
+eval create table t1(a int, index $key_type (a));
insert into t1 values (1), (2), (3);
handler t1 open;
--error 1054
@@ -164,7 +251,7 @@ drop table t1;
#
# BUG#3649
#
-create table t1 ( a int, b int, INDEX a (a) );
+eval create table t1 ( a int, b int, INDEX a $key_type (a) );
insert into t1 values (1,2), (2,1);
handler t1 open;
handler t1 read a=(1) where b=2;
@@ -174,143 +261,6 @@ handler t1 close;
drop table t1;
#
-# Check if two database names beginning the same are seen as different.
-#
-# This database begins like the usual 'test' database.
-#
---disable_warnings
-drop database if exists test_test;
---enable_warnings
-create database test_test;
-use test_test;
-create table t1(table_id char(20) primary key);
-insert into t1 values ('test_test.t1');
-insert into t1 values ('');
-handler t1 open;
-handler t1 read first limit 9;
-create table t2(table_id char(20) primary key);
-insert into t2 values ('test_test.t2');
-insert into t2 values ('');
-handler t2 open;
-handler t2 read first limit 9;
-#
-# This is the usual 'test' database.
-#
-use test;
---disable_warnings
-drop table if exists t1;
---enable_warnings
-create table t1(table_id char(20) primary key);
-insert into t1 values ('test.t1');
-insert into t1 values ('');
---error 1066
-handler t1 open;
-#
-# Check accesibility of all the tables.
-#
-use test;
---error 1064
-handler test.t1 read first limit 9;
---error 1064
-handler test_test.t1 read first limit 9;
-handler t1 read first limit 9;
---error 1064
-handler test_test.t2 read first limit 9;
-handler t2 read first limit 9;
-
-#
-# Cleanup.
-#
-
---error 1064
-handler test_test.t1 close;
-handler t1 close;
-drop table test_test.t1;
---error 1064
-handler test_test.t2 close;
-handler t2 close;
-drop table test_test.t2;
-drop database test_test;
-
-#
-use test;
---error 1064
-handler test.t1 close;
---error 1109
-handler t1 close;
-drop table test.t1;
-
-#
-# BUG#4335
-#
---disable_warnings
-drop database if exists test_test;
-drop table if exists t1;
-drop table if exists t2;
-drop table if exists t3;
---enable_warnings
-create database test_test;
-use test_test;
-create table t1 (c1 char(20));
-insert into t1 values ('test_test.t1');
-create table t3 (c1 char(20));
-insert into t3 values ('test_test.t3');
-handler t1 open;
-handler t1 read first limit 9;
-handler t1 open h1;
-handler h1 read first limit 9;
-use test;
-create table t1 (c1 char(20));
-create table t2 (c1 char(20));
-create table t3 (c1 char(20));
-insert into t1 values ('t1');
-insert into t2 values ('t2');
-insert into t3 values ('t3');
---error 1066
-handler t1 open;
---error 1066
-handler t2 open t1;
---error 1066
-handler t3 open t1;
-handler t1 read first limit 9;
---error 1064
-handler test.t1 close;
---error 1066
-handler test.t1 open h1;
---error 1066
-handler test_test.t1 open h1;
-handler test_test.t3 open h3;
-handler test.t1 open h2;
-handler t1 read first limit 9;
-handler h1 read first limit 9;
-handler h2 read first limit 9;
-handler h3 read first limit 9;
-handler h2 read first limit 9;
---error 1064
-handler test.h1 close;
-handler t1 close;
-handler h1 close;
-handler h2 close;
---error 1109
-handler t1 read first limit 9;
---error 1109
-handler h1 read first limit 9;
---error 1109
-handler h2 read first limit 9;
-handler h3 read first limit 9;
-handler h3 read first limit 9;
-use test_test;
-handler h3 read first limit 9;
---error 1064
-handler test.h3 read first limit 9;
-handler h3 close;
-use test;
-drop table t3;
-drop table t2;
-drop table t1;
-drop database test_test;
-
-#
# Test if fix for BUG#4286 correctly closes handler tables.
#
create table t1 (c1 char(20));
@@ -410,11 +360,13 @@ reap;
connection default;
drop table t1;
-CREATE TABLE t1 ( no1 smallint(5) NOT NULL default '0', no2 int(10) NOT NULL default '0', PRIMARY KEY (no1,no2));
+eval CREATE TABLE t1 ( no1 smallint(5) NOT NULL default '0', no2 int(10) NOT NULL default '0', PRIMARY KEY $key_type (no1,no2));
INSERT INTO t1 VALUES (1,274),(1,275),(2,6),(2,8),(4,1),(4,2);
HANDLER t1 OPEN;
HANDLER t1 READ `primary` = (1, 1000);
HANDLER t1 READ `primary` PREV;
+HANDLER t1 READ `primary` = (1, 1000);
+HANDLER t1 READ `primary` NEXT;
DROP TABLE t1;
# End of 4.1 tests
@@ -468,10 +420,7 @@ drop table if exists t1;
#
# Bug#25856 - HANDLER table OPEN in one connection lock DROP TABLE in another one
#
---disable_warnings
-drop table if exists t1;
---enable_warnings
-eval create table t1 (a int) ENGINE=$other_engine_type;
+eval create table t1 (a int not null) ENGINE=$other_engine_type;
--echo --> client 2
connection con2;
--error 1031
@@ -484,9 +433,6 @@ disconnect con2;
#
# Bug#30632 HANDLER read failure causes hang
#
---disable_warnings
-drop table if exists t1;
---enable_warnings
create table t1 (a int);
handler t1 open as t1_alias;
--error 1176
@@ -501,89 +447,18 @@ handler t1_alias close;
drop table t1;
#
-# Bug#21587 FLUSH TABLES causes server crash when used with HANDLER statements
-#
-
---disable_warnings
-drop table if exists t1,t2;
---enable_warnings
-create table t1 (c1 int);
-create table t2 (c1 int);
-insert into t1 values (1);
-insert into t2 values (2);
---echo connection: default
-handler t1 open;
-handler t1 read first;
-connect (flush,localhost,root,,);
-connection flush;
---echo connection: flush
---send flush tables;
-connection default;
---echo connection: default
-let $wait_condition=
- select count(*) = 1 from information_schema.processlist
- where state = "Flushing tables";
---source include/wait_condition.inc
-handler t2 open;
-handler t2 read first;
-handler t1 read next;
-handler t1 close;
-handler t2 close;
-connection flush;
-reap;
-connection default;
-drop table t1,t2;
-disconnect flush;
-
-#
-# Bug#31409 RENAME TABLE causes server crash or deadlock when used with HANDLER statements
-#
-
---disable_warnings
-drop table if exists t1,t2;
---enable_warnings
-create table t1 (c1 int);
---echo connection: default
-handler t1 open;
-handler t1 read first;
-connect (flush,localhost,root,,);
-connection flush;
---echo connection: flush
---send rename table t1 to t2;
-connection default;
---echo connection: default
-let $wait_condition=
- select count(*) = 1 from information_schema.processlist
- where state = "Waiting for table" and info = "rename table t1 to t2";
---source include/wait_condition.inc
-handler t2 open;
-handler t2 read first;
---error ER_NO_SUCH_TABLE
-handler t1 read next;
-handler t1 close;
-handler t2 close;
-connection flush;
-reap;
-connection default;
-drop table t2;
-disconnect flush;
-
-#
# Bug#30882 Dropping a temporary table inside a stored function may cause a server crash
#
# Test HANDLER statements in conjunction with temporary tables. While the temporary table
# is open by a HANDLER, no other statement can access it.
#
---disable_warnings
-drop table if exists t1;
---enable_warnings
-create temporary table t1 (a int, b char(1), key a(a), key b(a,b));
+eval create temporary table t1 (a int, b char(1), key a $key_type (a), key b $key_type (a,b));
insert into t1 values (0,"a"),(1,"b"),(2,"c"),(3,"d"),(4,"e"),
- (5,"f"),(6,"g"),(7,"h"),(8,"i"),(9,"j");
+ (5,"f"),(6,"g"),(7,"h"),(8,"i"),(9,"j"),(9,'k');
select a,b from t1;
handler t1 open as a1;
-handler a1 read a first;
+handler a1 read a=(1);
handler a1 read a next;
handler a1 read a next;
--error ER_CANT_REOPEN_TABLE
@@ -594,41 +469,19 @@ handler a1 read a=(6) where b="g";
handler a1 close;
select a,b from t1;
handler t1 open as a2;
-handler a2 read a first;
-handler a2 read a last;
-handler a2 read a prev;
+handler a2 read b=(9);
+handler a2 read b next;
+handler a2 read b prev limit 2;
+--error 0,1031
+handler a2 read b last;
+handler a2 read b prev;
handler a2 close;
drop table t1;
-#
-# Bug#31397 Inconsistent drop table behavior of handler tables.
-#
-
---disable_warnings
-drop table if exists t1,t2;
---enable_warnings
-create table t1 (a int);
-handler t1 open as t1_alias;
-drop table t1;
-create table t1 (a int);
-handler t1 open as t1_alias;
-flush tables;
-drop table t1;
-create table t1 (a int);
-handler t1 open as t1_alias;
-handler t1_alias close;
-drop table t1;
-create table t1 (a int);
-handler t1 open as t1_alias;
-handler t1_alias read first;
-drop table t1;
---error ER_UNKNOWN_TABLE
-handler t1_alias read next;
-
# Test that temporary tables associated with handlers are properly dropped.
create table t1 (a int);
-create temporary table t2 (a int, key(a));
+eval create temporary table t2 (a int, key $key_type (a));
handler t1 open as a1;
handler t2 open as a2;
handler a2 read a first;
@@ -640,7 +493,7 @@ handler a1 close;
# Alter table drop handlers
-create table t1 (a int, key(a));
+eval create table t1 (a int, key $key_type (a));
create table t2 like t1;
handler t1 open as a1;
handler t2 open as a2;
@@ -654,7 +507,7 @@ drop table t1, t2;
# Rename table drop handlers
-create table t1 (a int, key(a));
+eval create table t1 (a int, key $key_type (a));
handler t1 open as a1;
handler a1 read a first;
rename table t1 to t2;
@@ -664,7 +517,7 @@ drop table t2;
# Optimize table drop handlers
-create table t1 (a int, key(a));
+eval create table t1 (a int, key $key_type (a));
create table t2 like t1;
handler t1 open as a1;
handler t2 open as a2;
@@ -676,56 +529,14 @@ handler a1 close;
handler a2 close;
drop table t1, t2;
-# Flush tables causes handlers reopen
-
-create table t1 (a int, b char(1), key a(a), key b(a,b));
-insert into t1 values (0,"a"),(1,"b"),(2,"c"),(3,"d"),(4,"e"),
- (5,"f"),(6,"g"),(7,"h"),(8,"i"),(9,"j");
-handler t1 open;
-handler t1 read a first;
-handler t1 read a next;
-flush tables;
-handler t1 read a next;
-handler t1 read a next;
-flush tables with read lock;
-handler t1 read a next;
-unlock tables;
-drop table t1;
---error ER_UNKNOWN_TABLE
-handler t1 read a next;
-
-#
-# Bug#41110: crash with handler command when used concurrently with alter table
-# Bug#41112: crash in mysql_ha_close_table/get_lock_data with alter table
-#
-
---disable_warnings
-drop table if exists t1;
---enable_warnings
-create table t1 (a int);
-insert into t1 values (1);
-handler t1 open;
-connect(con1,localhost,root,,);
-send alter table t1 engine=memory;
-connection default;
-let $wait_condition=
- select count(*) = 1 from information_schema.processlist
- where state = "rename result table" and info = "alter table t1 engine=memory";
---source include/wait_condition.inc
---error ER_ILLEGAL_HA
-handler t1 read a next;
-handler t1 close;
-connection con1;
---reap
-drop table t1;
-disconnect con1;
---source include/wait_until_disconnected.inc
-connection default;
-
-#
-# Bug#44151 using handler commands on information_schema tables crashes server
-#
-USE information_schema;
---error ER_WRONG_USAGE
-HANDLER COLUMNS OPEN;
-USE test;
+--echo #
+--echo # BUG#51877 - HANDLER interface causes invalid memory read
+--echo #
+eval CREATE TABLE t1(a INT, KEY $key_type (a));
+HANDLER t1 OPEN;
+HANDLER t1 READ a FIRST;
+INSERT INTO t1 VALUES(1);
+--error 0,ER_CHECKREAD
+HANDLER t1 READ a NEXT;
+HANDLER t1 CLOSE;
+DROP TABLE t1;
diff --git a/mysql-test/suite/handler/heap.result b/mysql-test/suite/handler/heap.result
new file mode 100644
index 00000000000..b41f49091f1
--- /dev/null
+++ b/mysql-test/suite/handler/heap.result
@@ -0,0 +1,786 @@
+SET SESSION STORAGE_ENGINE = MEMORY;
+drop table if exists t1,t3,t4,t5;
+create table t1 (a int, b char(10), key a using btree (a), key b using btree (a,b));
+insert into t1 values
+(17,"ddd"),(18,"eee"),(19,"fff"),(19,"yyy"),
+(14,"aaa"),(16,"ccc"),(16,"xxx"),
+(20,"ggg"),(21,"hhh"),(22,"iii");
+handler t1 open as t2;
+handler t2 read b first;
+a b
+14 aaa
+handler t2 read b next;
+a b
+16 ccc
+handler t2 read b next;
+a b
+16 xxx
+handler t2 read b prev;
+a b
+16 ccc
+handler t2 read b last;
+a b
+22 iii
+handler t2 read b prev;
+a b
+21 hhh
+handler t2 read b prev;
+a b
+20 ggg
+handler t2 read b first;
+a b
+14 aaa
+handler t2 read b prev;
+a b
+handler t2 read b last;
+a b
+22 iii
+handler t2 read b prev;
+a b
+21 hhh
+handler t2 read b next;
+a b
+22 iii
+handler t2 read b next;
+a b
+handler t2 read a=(15);
+a b
+handler t2 read a=(21);
+a b
+21 hhh
+handler t2 read a=(19,"fff");
+ERROR 42000: Too many key parts specified; max 1 parts allowed
+handler t2 read b=(19,"fff");
+a b
+19 fff
+handler t2 read b=(19,"yyy");
+a b
+19 yyy
+handler t2 read b=(19);
+a b
+19 fff
+handler t1 read a last;
+ERROR 42S02: Unknown table 't1' in HANDLER
+handler t2 read a=(11);
+a b
+handler t2 read a>=(11);
+a b
+14 aaa
+handler t2 read b=(18);
+a b
+18 eee
+handler t2 read b>=(18);
+a b
+18 eee
+handler t2 read b>(18);
+a b
+19 fff
+handler t2 read b<=(18);
+a b
+18 eee
+handler t2 read b<(18);
+a b
+17 ddd
+handler t2 read a=(15);
+a b
+handler t2 read a>=(15) limit 2;
+a b
+16 ccc
+16 xxx
+handler t2 read a>(15) limit 2;
+a b
+16 ccc
+16 xxx
+handler t2 read a<=(15);
+a b
+14 aaa
+handler t2 read a<(15);
+a b
+14 aaa
+handler t2 read a=(54);
+a b
+handler t2 read a>=(54);
+a b
+handler t2 read a>(54);
+a b
+handler t2 read a<=(54);
+a b
+22 iii
+handler t2 read a<(54);
+a b
+22 iii
+handler t2 read a=(1);
+a b
+handler t2 read a>=(1);
+a b
+14 aaa
+handler t2 read a>(1);
+a b
+14 aaa
+handler t2 read a<=(1);
+a b
+handler t2 read a<(1);
+a b
+handler t2 read b first limit 5;
+a b
+14 aaa
+16 ccc
+16 xxx
+17 ddd
+18 eee
+handler t2 read b next limit 3;
+a b
+19 fff
+19 yyy
+20 ggg
+handler t2 read b prev limit 10;
+a b
+19 yyy
+19 fff
+18 eee
+17 ddd
+16 xxx
+16 ccc
+14 aaa
+handler t2 read b>=(16) limit 4;
+a b
+16 ccc
+16 xxx
+17 ddd
+18 eee
+handler t2 read b>=(16) limit 2,2;
+a b
+17 ddd
+18 eee
+select * from t1 where a>=16 order by a,b limit 2,2;
+a b
+17 ddd
+18 eee
+handler t2 read a last limit 3;
+a b
+22 iii
+21 hhh
+20 ggg
+handler t2 read b=(16) limit 1,3;
+a b
+16 xxx
+handler t2 read b=(19);
+a b
+19 fff
+handler t2 read b=(19) where b="yyy";
+a b
+19 yyy
+handler t2 read first;
+a b
+17 ddd
+handler t2 read next;
+a b
+18 eee
+handler t2 read next;
+a b
+19 fff
+handler t2 close;
+handler t1 open;
+handler t1 read b next;
+a b
+14 aaa
+handler t1 read b next;
+a b
+16 ccc
+handler t1 close;
+handler t1 open;
+handler t1 read a prev;
+a b
+22 iii
+handler t1 read a prev;
+a b
+21 hhh
+handler t1 close;
+handler t1 open as t2;
+handler t2 read first;
+a b
+17 ddd
+alter table t1 engine = MEMORY;
+handler t2 read first;
+ERROR 42S02: Unknown table 't2' in HANDLER
+handler t1 open;
+handler t1 read a=(16) limit 1,3;
+a b
+16 xxx
+flush tables;
+handler t1 read a=(16) limit 1,3;
+a b
+16 xxx
+handler t1 close;
+handler t1 open;
+prepare stmt from 'handler t1 read a=(?) limit ?,?';
+set @a=16,@b=1,@c=100;
+execute stmt using @a,@b,@c;
+a b
+16 xxx
+set @a=16,@b=2,@c=1;
+execute stmt using @a,@b,@c;
+a b
+set @a=16,@b=0,@c=2;
+execute stmt using @a,@b,@c;
+a b
+16 ccc
+16 xxx
+deallocate prepare stmt;
+prepare stmt from 'handler t1 read a next limit ?';
+handler t1 read a>=(11);
+a b
+14 aaa
+set @a=3;
+execute stmt using @a;
+a b
+16 ccc
+16 xxx
+17 ddd
+execute stmt using @a;
+a b
+18 eee
+19 fff
+19 yyy
+execute stmt using @a;
+a b
+20 ggg
+21 hhh
+22 iii
+deallocate prepare stmt;
+prepare stmt from 'handler t1 read b prev limit ?';
+execute stmt using @a;
+a b
+22 iii
+21 hhh
+20 ggg
+execute stmt using @a;
+a b
+19 yyy
+19 fff
+18 eee
+execute stmt using @a;
+a b
+17 ddd
+16 xxx
+16 ccc
+execute stmt using @a;
+a b
+14 aaa
+deallocate prepare stmt;
+prepare stmt from 'handler t1 read b=(?,?)';
+set @a=14, @b='aaa';
+execute stmt using @a,@b;
+a b
+14 aaa
+set @a=14, @b='not found';
+execute stmt using @a,@b;
+a b
+deallocate prepare stmt;
+prepare stmt from 'handler t1 read b=(1+?) limit 10';
+set @a=15;
+execute stmt using @a;
+a b
+16 ccc
+16 xxx
+execute stmt using @a;
+a b
+16 ccc
+16 xxx
+deallocate prepare stmt;
+prepare stmt from 'handler t1 read a>=(?) where a < ? limit 5';
+set @a=15, @b=20;
+execute stmt using @a,@b;
+a b
+16 ccc
+16 xxx
+17 ddd
+18 eee
+19 fff
+execute stmt using @a,@b;
+a b
+16 ccc
+16 xxx
+17 ddd
+18 eee
+19 fff
+deallocate prepare stmt;
+prepare stmt from 'handler t1 read a=(?)';
+set @a=16;
+execute stmt using @a;
+a b
+16 ccc
+alter table t1 add c int;
+execute stmt using @a;
+ERROR 42S02: Unknown table 't1' in HANDLER
+deallocate prepare stmt;
+handler t1 close;
+ERROR 42S02: Unknown table 't1' in HANDLER
+handler t1 open;
+prepare stmt from 'handler t1 read a=(?)';
+flush tables;
+set @a=16;
+execute stmt using @a;
+ERROR HY000: Prepared statement needs to be re-prepared
+deallocate prepare stmt;
+handler t1 close;
+handler t1 open as t2;
+drop table t1;
+create table t1 (a int not null);
+insert into t1 values (17);
+handler t2 read first;
+ERROR 42S02: Unknown table 't2' in HANDLER
+handler t1 open as t2;
+alter table t1 engine=CSV;
+handler t2 read first;
+ERROR 42S02: Unknown table 't2' in HANDLER
+drop table t1;
+create table t1 (a int);
+insert into t1 values (1),(2),(3),(4),(5),(6);
+delete from t1 limit 2;
+handler t1 open;
+handler t1 read first;
+a
+3
+handler t1 read first limit 1,1;
+a
+4
+handler t1 read first limit 2,2;
+a
+5
+6
+delete from t1 limit 3;
+handler t1 read first;
+a
+6
+drop table t1;
+create table t1(a int, index using btree (a));
+insert into t1 values (1), (2), (3);
+handler t1 open;
+handler t1 read a=(W);
+ERROR 42S22: Unknown column 'W' in 'field list'
+handler t1 read a=(a);
+ERROR HY000: Incorrect arguments to HANDLER ... READ
+drop table t1;
+create table t1 (a char(5));
+insert into t1 values ("Ok");
+handler t1 open as t;
+handler t read first;
+a
+Ok
+use mysql;
+handler t read first;
+a
+Ok
+handler t close;
+handler test.t1 open as t;
+handler t read first;
+a
+Ok
+handler t close;
+use test;
+drop table t1;
+create table t1 ( a int, b int, INDEX a using btree (a) );
+insert into t1 values (1,2), (2,1);
+handler t1 open;
+handler t1 read a=(1) where b=2;
+a b
+1 2
+handler t1 read a=(1) where b=3;
+a b
+handler t1 read a=(1) where b=1;
+a b
+handler t1 close;
+drop table t1;
+create table t1 (c1 char(20));
+insert into t1 values ("t1");
+handler t1 open as h1;
+handler h1 read first limit 9;
+c1
+t1
+create table t2 (c1 char(20));
+insert into t2 values ("t2");
+handler t2 open as h2;
+handler h2 read first limit 9;
+c1
+t2
+create table t3 (c1 char(20));
+insert into t3 values ("t3");
+handler t3 open as h3;
+handler h3 read first limit 9;
+c1
+t3
+create table t4 (c1 char(20));
+insert into t4 values ("t4");
+handler t4 open as h4;
+handler h4 read first limit 9;
+c1
+t4
+create table t5 (c1 char(20));
+insert into t5 values ("t5");
+handler t5 open as h5;
+handler h5 read first limit 9;
+c1
+t5
+alter table t1 engine=MyISAM;
+handler h1 read first limit 9;
+ERROR 42S02: Unknown table 'h1' in HANDLER
+handler h2 read first limit 9;
+c1
+t2
+handler h3 read first limit 9;
+c1
+t3
+handler h4 read first limit 9;
+c1
+t4
+handler h5 read first limit 9;
+c1
+t5
+alter table t5 engine=MyISAM;
+handler h1 read first limit 9;
+ERROR 42S02: Unknown table 'h1' in HANDLER
+handler h2 read first limit 9;
+c1
+t2
+handler h3 read first limit 9;
+c1
+t3
+handler h4 read first limit 9;
+c1
+t4
+handler h5 read first limit 9;
+ERROR 42S02: Unknown table 'h5' in HANDLER
+alter table t3 engine=MyISAM;
+handler h1 read first limit 9;
+ERROR 42S02: Unknown table 'h1' in HANDLER
+handler h2 read first limit 9;
+c1
+t2
+handler h3 read first limit 9;
+ERROR 42S02: Unknown table 'h3' in HANDLER
+handler h4 read first limit 9;
+c1
+t4
+handler h5 read first limit 9;
+ERROR 42S02: Unknown table 'h5' in HANDLER
+handler h2 close;
+handler h4 close;
+handler t1 open as h1_1;
+handler t1 open as h1_2;
+handler t1 open as h1_3;
+handler h1_1 read first limit 9;
+c1
+t1
+handler h1_2 read first limit 9;
+c1
+t1
+handler h1_3 read first limit 9;
+c1
+t1
+alter table t1 engine=MEMORY;
+handler h1_1 read first limit 9;
+ERROR 42S02: Unknown table 'h1_1' in HANDLER
+handler h1_2 read first limit 9;
+ERROR 42S02: Unknown table 'h1_2' in HANDLER
+handler h1_3 read first limit 9;
+ERROR 42S02: Unknown table 'h1_3' in HANDLER
+drop table t1;
+drop table t2;
+drop table t3;
+drop table t4;
+drop table t5;
+create table t1 (c1 int);
+insert into t1 values (1);
+handler t1 open;
+handler t1 read first;
+c1
+1
+send the below to another connection, do not wait for the result
+optimize table t1;
+proceed with the normal connection
+handler t1 read next;
+c1
+1
+handler t1 close;
+read the result from the other connection
+Table Op Msg_type Msg_text
+test.t1 optimize note The storage engine for the table doesn't support optimize
+proceed with the normal connection
+drop table t1;
+CREATE TABLE t1 ( no1 smallint(5) NOT NULL default '0', no2 int(10) NOT NULL default '0', PRIMARY KEY using btree (no1,no2));
+INSERT INTO t1 VALUES (1,274),(1,275),(2,6),(2,8),(4,1),(4,2);
+HANDLER t1 OPEN;
+HANDLER t1 READ `primary` = (1, 1000);
+no1 no2
+HANDLER t1 READ `primary` PREV;
+no1 no2
+1 275
+HANDLER t1 READ `primary` = (1, 1000);
+no1 no2
+HANDLER t1 READ `primary` NEXT;
+no1 no2
+2 6
+DROP TABLE t1;
+create table t1 (c1 int);
+insert into t1 values (14397);
+flush tables with read lock;
+drop table t1;
+ERROR HY000: Can't execute the query because you have a conflicting read lock
+send the below to another connection, do not wait for the result
+drop table t1;
+proceed with the normal connection
+select * from t1;
+c1
+14397
+unlock tables;
+read the result from the other connection
+proceed with the normal connection
+select * from t1;
+ERROR 42S02: Table 'test.t1' doesn't exist
+drop table if exists t1;
+Warnings:
+Note 1051 Unknown table 't1'
+create table t1 (a int not null) ENGINE=CSV;
+--> client 2
+handler t1 open;
+ERROR HY000: Table storage engine for 't1' doesn't have this option
+--> client 1
+drop table t1;
+create table t1 (a int);
+handler t1 open as t1_alias;
+handler t1_alias read a next;
+ERROR 42000: Key 'a' doesn't exist in table 't1_alias'
+handler t1_alias READ a next where inexistent > 0;
+ERROR 42S22: Unknown column 'inexistent' in 'field list'
+handler t1_alias read a next;
+ERROR 42000: Key 'a' doesn't exist in table 't1_alias'
+handler t1_alias READ a next where inexistent > 0;
+ERROR 42S22: Unknown column 'inexistent' in 'field list'
+handler t1_alias close;
+drop table t1;
+create temporary table t1 (a int, b char(1), key a using btree (a), key b using btree (a,b));
+insert into t1 values (0,"a"),(1,"b"),(2,"c"),(3,"d"),(4,"e"),
+(5,"f"),(6,"g"),(7,"h"),(8,"i"),(9,"j"),(9,'k');
+select a,b from t1;
+a b
+0 a
+1 b
+2 c
+3 d
+4 e
+5 f
+6 g
+7 h
+8 i
+9 j
+9 k
+handler t1 open as a1;
+handler a1 read a=(1);
+a b
+1 b
+handler a1 read a next;
+a b
+2 c
+handler a1 read a next;
+a b
+3 d
+select a,b from t1;
+ERROR HY000: Can't reopen table: 'a1'
+handler a1 read a prev;
+a b
+2 c
+handler a1 read a prev;
+a b
+1 b
+handler a1 read a=(6) where b="g";
+a b
+6 g
+handler a1 close;
+select a,b from t1;
+a b
+0 a
+1 b
+2 c
+3 d
+4 e
+5 f
+6 g
+7 h
+8 i
+9 j
+9 k
+handler t1 open as a2;
+handler a2 read b=(9);
+a b
+9 j
+handler a2 read b next;
+a b
+9 k
+handler a2 read b prev limit 2;
+a b
+9 j
+8 i
+handler a2 read b last;
+a b
+9 k
+handler a2 read b prev;
+a b
+9 j
+handler a2 close;
+drop table t1;
+create table t1 (a int);
+create temporary table t2 (a int, key using btree (a));
+handler t1 open as a1;
+handler t2 open as a2;
+handler a2 read a first;
+a
+drop table t1, t2;
+handler a2 read a next;
+ERROR 42S02: Unknown table 'a2' in HANDLER
+handler a1 close;
+ERROR 42S02: Unknown table 'a1' in HANDLER
+create table t1 (a int, key using btree (a));
+create table t2 like t1;
+handler t1 open as a1;
+handler t2 open as a2;
+handler a1 read a first;
+a
+handler a2 read a first;
+a
+alter table t1 add b int;
+handler a1 close;
+ERROR 42S02: Unknown table 'a1' in HANDLER
+handler a2 close;
+drop table t1, t2;
+create table t1 (a int, key using btree (a));
+handler t1 open as a1;
+handler a1 read a first;
+a
+rename table t1 to t2;
+handler a1 read a first;
+ERROR 42S02: Unknown table 'a1' in HANDLER
+drop table t2;
+create table t1 (a int, key using btree (a));
+create table t2 like t1;
+handler t1 open as a1;
+handler t2 open as a2;
+handler a1 read a first;
+a
+handler a2 read a first;
+a
+optimize table t1;
+Table Op Msg_type Msg_text
+test.t1 optimize note The storage engine for the table doesn't support optimize
+handler a1 close;
+ERROR 42S02: Unknown table 'a1' in HANDLER
+handler a2 close;
+drop table t1, t2;
+#
+# BUG#51877 - HANDLER interface causes invalid memory read
+#
+CREATE TABLE t1(a INT, KEY using btree (a));
+HANDLER t1 OPEN;
+HANDLER t1 READ a FIRST;
+a
+INSERT INTO t1 VALUES(1);
+HANDLER t1 READ a NEXT;
+HANDLER t1 CLOSE;
+DROP TABLE t1;
+CREATE TABLE t1(a INT, b INT, KEY(a), KEY b using btree (b), KEY ab using btree(a, b));
+INSERT INTO t1 VALUES (2, 20), (2,20), (1, 10), (4, 40), (3, 30), (5,50), (6,50);
+HANDLER t1 OPEN;
+HANDLER t1 READ a>=(2) limit 3;
+a b
+2 20
+2 20
+HANDLER t1 READ a PREV;
+a b
+2 20
+HANDLER t1 READ a PREV;
+a b
+2 20
+HANDLER t1 READ a PREV;
+a b
+HANDLER t1 READ b>=(20) limit 3;
+a b
+2 20
+2 20
+3 30
+HANDLER t1 READ b PREV;
+a b
+2 20
+HANDLER t1 READ b PREV LIMIT 2;
+a b
+2 20
+1 10
+HANDLER t1 READ ab=(3,30) limit 3;
+a b
+3 30
+HANDLER t1 READ ab>=(3,30) limit 3;
+a b
+3 30
+4 40
+5 50
+HANDLER t1 READ a FIRST;
+ERROR HY000: Table storage engine for 't1' doesn't have this option
+HANDLER t1 READ a LAST;
+ERROR HY000: Table storage engine for 't1' doesn't have this option
+HANDLER t1 READ b FIRST LIMIT 2;
+a b
+1 10
+2 20
+HANDLER t1 READ b LAST LIMIT 2;
+a b
+6 50
+5 50
+HANDLER t1 READ FIRST LIMIT 10;
+a b
+2 20
+2 20
+1 10
+4 40
+3 30
+5 50
+6 50
+HANDLER t1 READ b FIRST;
+a b
+1 10
+insert into t1 values (7,50);
+HANDLER t1 READ b NEXT;
+ERROR HY000: Record has changed since last read in table 't1'
+HANDLER t1 READ b FIRST;
+a b
+1 10
+insert into t1 values (7,50);
+HANDLER t1 READ b NEXT;
+ERROR HY000: Record has changed since last read in table 't1'
+HANDLER t1 READ FIRST;
+a b
+2 20
+insert into t1 values (8,50);
+HANDLER t1 READ NEXT;
+a b
+2 20
+delete from t1 where a=3;
+HANDLER t1 READ NEXT LIMIT 2;
+a b
+1 10
+4 40
+delete from t1;
+HANDLER t1 READ NEXT LIMIT 2;
+ERROR HY000: Record has changed since last read in table 't1'
+HANDLER t1 CLOSE;
+DROP TABLE t1;
+create table t1 (f1 integer not null, key (f1)) engine=Memory;
+insert into t1 values (1);
+HANDLER t1 OPEN;
+HANDLER t1 READ f1 NEXT;
+ERROR HY000: Table storage engine for 't1' doesn't have this option
+HANDLER t1 READ f1 NEXT;
+ERROR HY000: Table storage engine for 't1' doesn't have this option
+HANDLER t1 READ f1 NEXT;
+ERROR HY000: Table storage engine for 't1' doesn't have this option
+HANDLER t1 CLOSE;
+DROP TABLE t1;
+End of 5.3 tests
diff --git a/mysql-test/suite/handler/heap.test b/mysql-test/suite/handler/heap.test
new file mode 100644
index 00000000000..e9e5c5fad44
--- /dev/null
+++ b/mysql-test/suite/handler/heap.test
@@ -0,0 +1,88 @@
+# test of HANDLER with HEAP tables
+#
+
+let $engine_type= MEMORY;
+let $key_type=using btree;
+
+--source init.inc
+--source handler.inc
+
+#
+# Test what happens if table is changed (Unique test for HEAP)
+#
+
+connect (con1,localhost,root,,);
+connection default;
+
+CREATE TABLE t1(a INT, b INT, KEY(a), KEY b using btree (b), KEY ab using btree(a, b));
+
+INSERT INTO t1 VALUES (2, 20), (2,20), (1, 10), (4, 40), (3, 30), (5,50), (6,50);
+
+HANDLER t1 OPEN;
+HANDLER t1 READ a>=(2) limit 3;
+HANDLER t1 READ a PREV;
+HANDLER t1 READ a PREV;
+HANDLER t1 READ a PREV;
+HANDLER t1 READ b>=(20) limit 3;
+HANDLER t1 READ b PREV;
+HANDLER t1 READ b PREV LIMIT 2;
+HANDLER t1 READ ab=(3,30) limit 3;
+HANDLER t1 READ ab>=(3,30) limit 3;
+
+# Test FIRST/LAST on hash and btree keys
+--error ER_ILLEGAL_HA
+HANDLER t1 READ a FIRST;
+--error ER_ILLEGAL_HA
+HANDLER t1 READ a LAST;
+HANDLER t1 READ b FIRST LIMIT 2;
+HANDLER t1 READ b LAST LIMIT 2;
+
+# Table scan
+HANDLER t1 READ FIRST LIMIT 10;
+# Index scan
+HANDLER t1 READ b FIRST;
+insert into t1 values (7,50);
+--error ER_CHECKREAD
+HANDLER t1 READ b NEXT;
+
+HANDLER t1 READ b FIRST;
+connection con1;
+insert into t1 values (7,50);
+connection default;
+--error ER_CHECKREAD
+HANDLER t1 READ b NEXT;
+
+HANDLER t1 READ FIRST;
+connection con1;
+insert into t1 values (8,50);
+connection default;
+HANDLER t1 READ NEXT;
+connection con1;
+delete from t1 where a=3;
+connection default;
+HANDLER t1 READ NEXT LIMIT 2;
+connection con1;
+delete from t1;
+connection default;
+--error ER_CHECKREAD
+HANDLER t1 READ NEXT LIMIT 2;
+HANDLER t1 CLOSE;
+DROP TABLE t1;
+disconnect con1;
+
+#
+# LP#702786 Two handler read f1 next gives different errors
+#
+create table t1 (f1 integer not null, key (f1)) engine=Memory;
+insert into t1 values (1);
+HANDLER t1 OPEN;
+--error 1031
+HANDLER t1 READ f1 NEXT;
+--error 1031
+HANDLER t1 READ f1 NEXT;
+--error 1031
+HANDLER t1 READ f1 NEXT;
+HANDLER t1 CLOSE;
+DROP TABLE t1;
+
+--echo End of 5.3 tests
diff --git a/mysql-test/suite/handler/init.inc b/mysql-test/suite/handler/init.inc
new file mode 100644
index 00000000000..32c6010f95b
--- /dev/null
+++ b/mysql-test/suite/handler/init.inc
@@ -0,0 +1,33 @@
+# Setup things for handler.inc
+#
+# Input variables
+# $engine_type -- storage engine to be tested
+# $key_type -- set if you want a non standard key type
+#
+# This scripts sets up default values for:
+# $other_engine_type -- storage engine <> $engine_type
+# $other_handler_engine_type -- storage engine <> $engine_type, if possible
+# 1. $other_handler_engine_type must support handler
+# 2. $other_handler_engine_type must point to an all
+# time available storage engine
+# have to be set before sourcing this script.
+#
+# Handler tests don't work with embedded server
+#
+-- source include/not_embedded.inc
+
+eval SET SESSION STORAGE_ENGINE = $engine_type;
+let $other_engine_type= CSV;
+let $other_handler_engine_type= MyISAM;
+
+--disable_warnings
+drop table if exists t1,t3,t4,t5;
+--enable_warnings
+
+# Create default test table
+
+eval create table t1 (a int, b char(10), key a $key_type (a), key b $key_type (a,b));
+insert into t1 values
+(17,"ddd"),(18,"eee"),(19,"fff"),(19,"yyy"),
+(14,"aaa"),(16,"ccc"),(16,"xxx"),
+(20,"ggg"),(21,"hhh"),(22,"iii");
diff --git a/mysql-test/r/handler_innodb.result b/mysql-test/suite/handler/innodb.result
index 957fc30acef..113d5d7e61f 100644
--- a/mysql-test/r/handler_innodb.result
+++ b/mysql-test/suite/handler/innodb.result
@@ -1,56 +1,53 @@
SET SESSION STORAGE_ENGINE = InnoDB;
drop table if exists t1,t3,t4,t5;
-create table t1 (a int, b char(10), key a(a), key b(a,b));
+create table t1 (a int, b char(10), key a (a), key b (a,b));
insert into t1 values
(17,"ddd"),(18,"eee"),(19,"fff"),(19,"yyy"),
-(14,"aaa"),(15,"bbb"),(16,"ccc"),(16,"xxx"),
+(14,"aaa"),(16,"ccc"),(16,"xxx"),
(20,"ggg"),(21,"hhh"),(22,"iii");
handler t1 open as t2;
-handler t2 read a=(SELECT 1);
-ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'SELECT 1)' at line 1
-handler t2 read a first;
+handler t2 read b first;
a b
14 aaa
-handler t2 read a next;
-a b
-15 bbb
-handler t2 read a next;
+handler t2 read b next;
a b
16 ccc
-handler t2 read a prev;
+handler t2 read b next;
a b
-15 bbb
-handler t2 read a last;
+16 xxx
+handler t2 read b prev;
+a b
+16 ccc
+handler t2 read b last;
a b
22 iii
-handler t2 read a prev;
+handler t2 read b prev;
a b
21 hhh
-handler t2 read a prev;
+handler t2 read b prev;
a b
20 ggg
-handler t2 read a first;
+handler t2 read b first;
a b
14 aaa
-handler t2 read a prev;
+handler t2 read b prev;
a b
-handler t2 read a last;
+handler t2 read b last;
a b
22 iii
-handler t2 read a prev;
+handler t2 read b prev;
a b
21 hhh
-handler t2 read a next;
+handler t2 read b next;
a b
22 iii
-handler t2 read a next;
+handler t2 read b next;
a b
handler t2 read a=(15);
a b
-15 bbb
-handler t2 read a=(16);
+handler t2 read a=(21);
a b
-16 ccc
+21 hhh
handler t2 read a=(19,"fff");
ERROR 42000: Too many key parts specified; max 1 parts allowed
handler t2 read b=(19,"fff");
@@ -69,49 +66,93 @@ a b
handler t2 read a>=(11);
a b
14 aaa
-handler t2 read a=(18);
+handler t2 read b=(18);
a b
18 eee
-handler t2 read a>=(18);
+handler t2 read b>=(18);
a b
18 eee
-handler t2 read a>(18);
+handler t2 read b>(18);
a b
19 fff
-handler t2 read a<=(18);
+handler t2 read b<=(18);
a b
18 eee
-handler t2 read a<(18);
+handler t2 read b<(18);
a b
17 ddd
-handler t2 read a first limit 5;
+handler t2 read a=(15);
+a b
+handler t2 read a>=(15) limit 2;
+a b
+16 ccc
+16 xxx
+handler t2 read a>(15) limit 2;
+a b
+16 ccc
+16 xxx
+handler t2 read a<=(15);
+a b
+14 aaa
+handler t2 read a<(15);
+a b
+14 aaa
+handler t2 read a=(54);
+a b
+handler t2 read a>=(54);
+a b
+handler t2 read a>(54);
+a b
+handler t2 read a<=(54);
+a b
+22 iii
+handler t2 read a<(54);
+a b
+22 iii
+handler t2 read a=(1);
+a b
+handler t2 read a>=(1);
+a b
+14 aaa
+handler t2 read a>(1);
+a b
+14 aaa
+handler t2 read a<=(1);
+a b
+handler t2 read a<(1);
+a b
+handler t2 read b first limit 5;
a b
14 aaa
-15 bbb
16 ccc
16 xxx
17 ddd
-handler t2 read a next limit 3;
-a b
18 eee
+handler t2 read b next limit 3;
+a b
19 fff
19 yyy
-handler t2 read a prev limit 10;
+20 ggg
+handler t2 read b prev limit 10;
a b
+19 yyy
19 fff
18 eee
17 ddd
16 xxx
16 ccc
-15 bbb
14 aaa
-handler t2 read a>=(16) limit 4;
+handler t2 read b>=(16) limit 4;
a b
16 ccc
16 xxx
17 ddd
18 eee
-handler t2 read a>=(16) limit 2,2;
+handler t2 read b>=(16) limit 2,2;
+a b
+17 ddd
+18 eee
+select * from t1 where a>=16 order by a,b limit 2,2;
a b
17 ddd
18 eee
@@ -120,10 +161,13 @@ a b
22 iii
21 hhh
20 ggg
-handler t2 read a=(19);
+handler t2 read b=(16) limit 1,3;
+a b
+16 xxx
+handler t2 read b=(19);
a b
19 fff
-handler t2 read a=(19) where b="yyy";
+handler t2 read b=(19) where b="yyy";
a b
19 yyy
handler t2 read first;
@@ -135,16 +179,14 @@ a b
handler t2 read next;
a b
19 fff
-handler t2 read last;
-ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '' at line 1
handler t2 close;
handler t1 open;
-handler t1 read a next;
+handler t1 read b next;
a b
14 aaa
-handler t1 read a next;
+handler t1 read b next;
a b
-15 bbb
+16 ccc
handler t1 close;
handler t1 open;
handler t1 read a prev;
@@ -161,14 +203,135 @@ a b
alter table t1 engine = InnoDB;
handler t2 read first;
ERROR 42S02: Unknown table 't2' in HANDLER
+handler t1 open;
+handler t1 read a=(16) limit 1,3;
+a b
+16 xxx
+flush tables;
+handler t1 read a=(16) limit 1,3;
+a b
+16 xxx
+handler t1 close;
+handler t1 open;
+prepare stmt from 'handler t1 read a=(?) limit ?,?';
+set @a=16,@b=1,@c=100;
+execute stmt using @a,@b,@c;
+a b
+16 xxx
+set @a=16,@b=2,@c=1;
+execute stmt using @a,@b,@c;
+a b
+set @a=16,@b=0,@c=2;
+execute stmt using @a,@b,@c;
+a b
+16 ccc
+16 xxx
+deallocate prepare stmt;
+prepare stmt from 'handler t1 read a next limit ?';
+handler t1 read a>=(11);
+a b
+14 aaa
+set @a=3;
+execute stmt using @a;
+a b
+16 ccc
+16 xxx
+17 ddd
+execute stmt using @a;
+a b
+18 eee
+19 fff
+19 yyy
+execute stmt using @a;
+a b
+20 ggg
+21 hhh
+22 iii
+deallocate prepare stmt;
+prepare stmt from 'handler t1 read b prev limit ?';
+execute stmt using @a;
+a b
+22 iii
+21 hhh
+20 ggg
+execute stmt using @a;
+a b
+19 yyy
+19 fff
+18 eee
+execute stmt using @a;
+a b
+17 ddd
+16 xxx
+16 ccc
+execute stmt using @a;
+a b
+14 aaa
+deallocate prepare stmt;
+prepare stmt from 'handler t1 read b=(?,?)';
+set @a=14, @b='aaa';
+execute stmt using @a,@b;
+a b
+14 aaa
+set @a=14, @b='not found';
+execute stmt using @a,@b;
+a b
+deallocate prepare stmt;
+prepare stmt from 'handler t1 read b=(1+?) limit 10';
+set @a=15;
+execute stmt using @a;
+a b
+16 ccc
+16 xxx
+execute stmt using @a;
+a b
+16 ccc
+16 xxx
+deallocate prepare stmt;
+prepare stmt from 'handler t1 read a>=(?) where a < ? limit 5';
+set @a=15, @b=20;
+execute stmt using @a,@b;
+a b
+16 ccc
+16 xxx
+17 ddd
+18 eee
+19 fff
+execute stmt using @a,@b;
+a b
+16 ccc
+16 xxx
+17 ddd
+18 eee
+19 fff
+deallocate prepare stmt;
+prepare stmt from 'handler t1 read a=(?)';
+set @a=16;
+execute stmt using @a;
+a b
+16 ccc
+alter table t1 add c int;
+execute stmt using @a;
+ERROR 42S02: Unknown table 't1' in HANDLER
+deallocate prepare stmt;
+handler t1 close;
+ERROR 42S02: Unknown table 't1' in HANDLER
+handler t1 open;
+prepare stmt from 'handler t1 read a=(?)';
+flush tables;
+set @a=16;
+execute stmt using @a;
+ERROR HY000: Prepared statement needs to be re-prepared
+deallocate prepare stmt;
+handler t1 close;
handler t1 open as t2;
drop table t1;
-create table t1 (a int);
+create table t1 (a int not null);
insert into t1 values (17);
handler t2 read first;
ERROR 42S02: Unknown table 't2' in HANDLER
handler t1 open as t2;
-alter table t1 engine=MEMORY;
+alter table t1 engine=CSV;
handler t2 read first;
ERROR 42S02: Unknown table 't2' in HANDLER
drop table t1;
@@ -191,7 +354,7 @@ handler t1 read first;
a
6
drop table t1;
-create table t1(a int, index(a));
+create table t1(a int, index (a));
insert into t1 values (1), (2), (3);
handler t1 open;
handler t1 read a=(W);
@@ -217,7 +380,7 @@ Ok
handler t close;
use test;
drop table t1;
-create table t1 ( a int, b int, INDEX a (a) );
+create table t1 ( a int, b int, INDEX a (a) );
insert into t1 values (1,2), (2,1);
handler t1 open;
handler t1 read a=(1) where b=2;
@@ -229,148 +392,6 @@ handler t1 read a=(1) where b=1;
a b
handler t1 close;
drop table t1;
-drop database if exists test_test;
-create database test_test;
-use test_test;
-create table t1(table_id char(20) primary key);
-insert into t1 values ('test_test.t1');
-insert into t1 values ('');
-handler t1 open;
-handler t1 read first limit 9;
-table_id
-
-test_test.t1
-create table t2(table_id char(20) primary key);
-insert into t2 values ('test_test.t2');
-insert into t2 values ('');
-handler t2 open;
-handler t2 read first limit 9;
-table_id
-
-test_test.t2
-use test;
-drop table if exists t1;
-create table t1(table_id char(20) primary key);
-insert into t1 values ('test.t1');
-insert into t1 values ('');
-handler t1 open;
-ERROR 42000: Not unique table/alias: 't1'
-use test;
-handler test.t1 read first limit 9;
-ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'read first limit 9' at line 1
-handler test_test.t1 read first limit 9;
-ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'read first limit 9' at line 1
-handler t1 read first limit 9;
-table_id
-
-test_test.t1
-handler test_test.t2 read first limit 9;
-ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'read first limit 9' at line 1
-handler t2 read first limit 9;
-table_id
-
-test_test.t2
-handler test_test.t1 close;
-ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'close' at line 1
-handler t1 close;
-drop table test_test.t1;
-handler test_test.t2 close;
-ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'close' at line 1
-handler t2 close;
-drop table test_test.t2;
-drop database test_test;
-use test;
-handler test.t1 close;
-ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'close' at line 1
-handler t1 close;
-ERROR 42S02: Unknown table 't1' in HANDLER
-drop table test.t1;
-drop database if exists test_test;
-drop table if exists t1;
-drop table if exists t2;
-drop table if exists t3;
-create database test_test;
-use test_test;
-create table t1 (c1 char(20));
-insert into t1 values ('test_test.t1');
-create table t3 (c1 char(20));
-insert into t3 values ('test_test.t3');
-handler t1 open;
-handler t1 read first limit 9;
-c1
-test_test.t1
-handler t1 open h1;
-handler h1 read first limit 9;
-c1
-test_test.t1
-use test;
-create table t1 (c1 char(20));
-create table t2 (c1 char(20));
-create table t3 (c1 char(20));
-insert into t1 values ('t1');
-insert into t2 values ('t2');
-insert into t3 values ('t3');
-handler t1 open;
-ERROR 42000: Not unique table/alias: 't1'
-handler t2 open t1;
-ERROR 42000: Not unique table/alias: 't1'
-handler t3 open t1;
-ERROR 42000: Not unique table/alias: 't1'
-handler t1 read first limit 9;
-c1
-test_test.t1
-handler test.t1 close;
-ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'close' at line 1
-handler test.t1 open h1;
-ERROR 42000: Not unique table/alias: 'h1'
-handler test_test.t1 open h1;
-ERROR 42000: Not unique table/alias: 'h1'
-handler test_test.t3 open h3;
-handler test.t1 open h2;
-handler t1 read first limit 9;
-c1
-test_test.t1
-handler h1 read first limit 9;
-c1
-test_test.t1
-handler h2 read first limit 9;
-c1
-t1
-handler h3 read first limit 9;
-c1
-test_test.t3
-handler h2 read first limit 9;
-c1
-t1
-handler test.h1 close;
-ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'close' at line 1
-handler t1 close;
-handler h1 close;
-handler h2 close;
-handler t1 read first limit 9;
-ERROR 42S02: Unknown table 't1' in HANDLER
-handler h1 read first limit 9;
-ERROR 42S02: Unknown table 'h1' in HANDLER
-handler h2 read first limit 9;
-ERROR 42S02: Unknown table 'h2' in HANDLER
-handler h3 read first limit 9;
-c1
-test_test.t3
-handler h3 read first limit 9;
-c1
-test_test.t3
-use test_test;
-handler h3 read first limit 9;
-c1
-test_test.t3
-handler test.h3 read first limit 9;
-ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'read first limit 9' at line 1
-handler h3 close;
-use test;
-drop table t3;
-drop table t2;
-drop table t1;
-drop database test_test;
create table t1 (c1 char(20));
insert into t1 values ("t1");
handler t1 open as h1;
@@ -496,6 +517,11 @@ no1 no2
HANDLER t1 READ `primary` PREV;
no1 no2
1 275
+HANDLER t1 READ `primary` = (1, 1000);
+no1 no2
+HANDLER t1 READ `primary` NEXT;
+no1 no2
+2 8
DROP TABLE t1;
create table t1 (c1 int);
insert into t1 values (14397);
@@ -516,14 +542,12 @@ ERROR 42S02: Table 'test.t1' doesn't exist
drop table if exists t1;
Warnings:
Note 1051 Unknown table 't1'
-drop table if exists t1;
-create table t1 (a int) ENGINE=MEMORY;
+create table t1 (a int not null) ENGINE=CSV;
--> client 2
handler t1 open;
ERROR HY000: Table storage engine for 't1' doesn't have this option
--> client 1
drop table t1;
-drop table if exists t1;
create table t1 (a int);
handler t1 open as t1_alias;
handler t1_alias read a next;
@@ -536,50 +560,9 @@ handler t1_alias READ a next where inexistent > 0;
ERROR 42S22: Unknown column 'inexistent' in 'field list'
handler t1_alias close;
drop table t1;
-drop table if exists t1,t2;
-create table t1 (c1 int);
-create table t2 (c1 int);
-insert into t1 values (1);
-insert into t2 values (2);
-connection: default
-handler t1 open;
-handler t1 read first;
-c1
-1
-connection: flush
-flush tables;;
-connection: default
-handler t2 open;
-handler t2 read first;
-c1
-2
-handler t1 read next;
-c1
-1
-handler t1 close;
-handler t2 close;
-drop table t1,t2;
-drop table if exists t1,t2;
-create table t1 (c1 int);
-connection: default
-handler t1 open;
-handler t1 read first;
-c1
-connection: flush
-rename table t1 to t2;;
-connection: default
-handler t2 open;
-handler t2 read first;
-c1
-handler t1 read next;
-ERROR 42S02: Table 'test.t1' doesn't exist
-handler t1 close;
-handler t2 close;
-drop table t2;
-drop table if exists t1;
-create temporary table t1 (a int, b char(1), key a(a), key b(a,b));
+create temporary table t1 (a int, b char(1), key a (a), key b (a,b));
insert into t1 values (0,"a"),(1,"b"),(2,"c"),(3,"d"),(4,"e"),
-(5,"f"),(6,"g"),(7,"h"),(8,"i"),(9,"j");
+(5,"f"),(6,"g"),(7,"h"),(8,"i"),(9,"j"),(9,'k');
select a,b from t1;
a b
0 a
@@ -592,24 +575,25 @@ a b
7 h
8 i
9 j
+9 k
handler t1 open as a1;
-handler a1 read a first;
-a b
-0 a
-handler a1 read a next;
+handler a1 read a=(1);
a b
1 b
handler a1 read a next;
a b
2 c
+handler a1 read a next;
+a b
+3 d
select a,b from t1;
ERROR HY000: Can't reopen table: 'a1'
handler a1 read a prev;
a b
-1 b
+2 c
handler a1 read a prev;
a b
-0 a
+1 b
handler a1 read a=(6) where b="g";
a b
6 g
@@ -626,39 +610,28 @@ a b
7 h
8 i
9 j
+9 k
handler t1 open as a2;
-handler a2 read a first;
-a b
-0 a
-handler a2 read a last;
+handler a2 read b=(9);
a b
9 j
-handler a2 read a prev;
+handler a2 read b next;
a b
+9 k
+handler a2 read b prev limit 2;
+a b
+9 j
8 i
+handler a2 read b last;
+a b
+9 k
+handler a2 read b prev;
+a b
+9 j
handler a2 close;
drop table t1;
-drop table if exists t1,t2;
-create table t1 (a int);
-handler t1 open as t1_alias;
-drop table t1;
-create table t1 (a int);
-handler t1 open as t1_alias;
-flush tables;
-drop table t1;
-create table t1 (a int);
-handler t1 open as t1_alias;
-handler t1_alias close;
-drop table t1;
-create table t1 (a int);
-handler t1 open as t1_alias;
-handler t1_alias read first;
-a
-drop table t1;
-handler t1_alias read next;
-ERROR 42S02: Unknown table 't1_alias' in HANDLER
create table t1 (a int);
-create temporary table t2 (a int, key(a));
+create temporary table t2 (a int, key (a));
handler t1 open as a1;
handler t2 open as a2;
handler a2 read a first;
@@ -668,7 +641,7 @@ handler a2 read a next;
ERROR 42S02: Unknown table 'a2' in HANDLER
handler a1 close;
ERROR 42S02: Unknown table 'a1' in HANDLER
-create table t1 (a int, key(a));
+create table t1 (a int, key (a));
create table t2 like t1;
handler t1 open as a1;
handler t2 open as a2;
@@ -681,7 +654,7 @@ handler a1 close;
ERROR 42S02: Unknown table 'a1' in HANDLER
handler a2 close;
drop table t1, t2;
-create table t1 (a int, key(a));
+create table t1 (a int, key (a));
handler t1 open as a1;
handler a1 read a first;
a
@@ -689,7 +662,7 @@ rename table t1 to t2;
handler a1 read a first;
ERROR 42S02: Unknown table 'a1' in HANDLER
drop table t2;
-create table t1 (a int, key(a));
+create table t1 (a int, key (a));
create table t2 like t1;
handler t1 open as a1;
handler t2 open as a2;
@@ -705,41 +678,25 @@ handler a1 close;
ERROR 42S02: Unknown table 'a1' in HANDLER
handler a2 close;
drop table t1, t2;
-create table t1 (a int, b char(1), key a(a), key b(a,b));
-insert into t1 values (0,"a"),(1,"b"),(2,"c"),(3,"d"),(4,"e"),
-(5,"f"),(6,"g"),(7,"h"),(8,"i"),(9,"j");
-handler t1 open;
-handler t1 read a first;
-a b
-0 a
-handler t1 read a next;
-a b
-1 b
-flush tables;
-handler t1 read a next;
-a b
-0 a
-handler t1 read a next;
-a b
-1 b
-flush tables with read lock;
-handler t1 read a next;
-a b
-0 a
-unlock tables;
-drop table t1;
-handler t1 read a next;
-ERROR 42S02: Unknown table 't1' in HANDLER
-drop table if exists t1;
-create table t1 (a int);
-insert into t1 values (1);
-handler t1 open;
-alter table t1 engine=memory;
-handler t1 read a next;
-ERROR HY000: Table storage engine for 't1' doesn't have this option
-handler t1 close;
-drop table t1;
-USE information_schema;
-HANDLER COLUMNS OPEN;
-ERROR HY000: Incorrect usage of HANDLER OPEN and information_schema
-USE test;
+#
+# BUG#51877 - HANDLER interface causes invalid memory read
+#
+CREATE TABLE t1(a INT, KEY (a));
+HANDLER t1 OPEN;
+HANDLER t1 READ a FIRST;
+a
+INSERT INTO t1 VALUES(1);
+HANDLER t1 READ a NEXT;
+a
+HANDLER t1 CLOSE;
+DROP TABLE t1;
+CREATE TABLE t1 (f1 integer, f2 integer, primary key (f1), key (f2)) engine=innodb;
+INSERT INTO t1 VALUES (1,1),(2,2),(3,3);
+HANDLER t1 OPEN;
+HANDLER t1 READ FIRST WHERE f2 <= 1;
+f1 f2
+1 1
+HANDLER t1 READ `PRIMARY` PREV;
+f1 f2
+3 3
+DROP TABLE t1;
diff --git a/mysql-test/suite/handler/innodb.test b/mysql-test/suite/handler/innodb.test
new file mode 100644
index 00000000000..6527c4bb8bb
--- /dev/null
+++ b/mysql-test/suite/handler/innodb.test
@@ -0,0 +1,28 @@
+# t/handler_innodb.test
+#
+# test of HANDLER ...
+#
+# Last update:
+# 2006-07-31 ML test refactored (MySQL 5.1)
+# code of t/handler.test and t/innodb_handler.test united
+# main testing code put into handler.inc
+# rename t/innodb_handler.test to t/handler_innodb.test
+#
+
+--source include/have_innodb.inc
+
+let $engine_type= InnoDB;
+
+--source init.inc
+--source handler.inc
+
+#
+# LP#697610 ha_index_prev(uchar*): Assertion `inited==INDEX'
+#
+
+CREATE TABLE t1 (f1 integer, f2 integer, primary key (f1), key (f2)) engine=innodb;
+INSERT INTO t1 VALUES (1,1),(2,2),(3,3);
+HANDLER t1 OPEN;
+HANDLER t1 READ FIRST WHERE f2 <= 1;
+HANDLER t1 READ `PRIMARY` PREV;
+DROP TABLE t1;
diff --git a/mysql-test/suite/handler/interface.result b/mysql-test/suite/handler/interface.result
new file mode 100644
index 00000000000..9949097c65a
--- /dev/null
+++ b/mysql-test/suite/handler/interface.result
@@ -0,0 +1,261 @@
+drop table if exists t1,t3,t4,t5;
+drop database if exists test_test;
+SET SESSION STORAGE_ENGINE = MyISAM;
+drop table if exists t1,t3,t4,t5;
+create table t1 (a int, b char(10), key a (a), key b (a,b));
+insert into t1 values
+(17,"ddd"),(18,"eee"),(19,"fff"),(19,"yyy"),
+(14,"aaa"),(16,"ccc"),(16,"xxx"),
+(20,"ggg"),(21,"hhh"),(22,"iii");
+handler t1 open;
+handler t1 read a=(SELECT 1);
+ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'SELECT 1)' at line 1
+handler t1 read a=(1) FIRST;
+ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'FIRST' at line 1
+handler t1 read a=(1) NEXT;
+ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'NEXT' at line 1
+handler t1 read last;
+ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '' at line 1
+handler t1 close;
+drop table t1;
+CREATE TABLE t1(a INT, PRIMARY KEY(a));
+insert into t1 values(1),(2);
+handler t1 open;
+handler t1 read primary=(1);
+ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'primary=(1)' at line 1
+handler t1 read `primary`=(1);
+a
+1
+handler t1 close;
+drop table t1;
+create database test_test;
+use test_test;
+create table t1(table_id char(20), primary key (table_id));
+insert into t1 values ('test_test.t1');
+insert into t1 values ('');
+handler t1 open;
+handler t1 read first limit 9;
+table_id
+test_test.t1
+
+create table t2(table_id char(20), primary key (table_id));
+insert into t2 values ('test_test.t2');
+insert into t2 values ('');
+handler t2 open;
+handler t2 read first limit 9;
+table_id
+test_test.t2
+
+use test;
+create table t1(table_id char(20), primary key (table_id));
+insert into t1 values ('test.t1');
+insert into t1 values ('');
+handler t1 open;
+ERROR 42000: Not unique table/alias: 't1'
+use test;
+handler test.t1 read first limit 9;
+ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'read first limit 9' at line 1
+handler test_test.t1 read first limit 9;
+ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'read first limit 9' at line 1
+handler t1 read first limit 9;
+table_id
+test_test.t1
+
+handler test_test.t2 read first limit 9;
+ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'read first limit 9' at line 1
+handler t2 read first limit 9;
+table_id
+test_test.t2
+
+handler test_test.t1 close;
+ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'close' at line 1
+handler t1 close;
+drop table test_test.t1;
+handler test_test.t2 close;
+ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'close' at line 1
+handler t2 close;
+drop table test_test.t2;
+drop database test_test;
+use test;
+handler test.t1 close;
+ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'close' at line 1
+handler t1 close;
+ERROR 42S02: Unknown table 't1' in HANDLER
+drop table test.t1;
+create database test_test;
+use test_test;
+create table t1 (c1 char(20));
+insert into t1 values ('test_test.t1');
+create table t3 (c1 char(20));
+insert into t3 values ('test_test.t3');
+handler t1 open;
+handler t1 read first limit 9;
+c1
+test_test.t1
+handler t1 open h1;
+handler h1 read first limit 9;
+c1
+test_test.t1
+use test;
+create table t1 (c1 char(20));
+create table t2 (c1 char(20));
+create table t3 (c1 char(20));
+insert into t1 values ('t1');
+insert into t2 values ('t2');
+insert into t3 values ('t3');
+handler t1 open;
+ERROR 42000: Not unique table/alias: 't1'
+handler t2 open t1;
+ERROR 42000: Not unique table/alias: 't1'
+handler t3 open t1;
+ERROR 42000: Not unique table/alias: 't1'
+handler t1 read first limit 9;
+c1
+test_test.t1
+handler test.t1 close;
+ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'close' at line 1
+handler test.t1 open h1;
+ERROR 42000: Not unique table/alias: 'h1'
+handler test_test.t1 open h1;
+ERROR 42000: Not unique table/alias: 'h1'
+handler test_test.t3 open h3;
+handler test.t1 open h2;
+handler t1 read first limit 9;
+c1
+test_test.t1
+handler h1 read first limit 9;
+c1
+test_test.t1
+handler h2 read first limit 9;
+c1
+t1
+handler h3 read first limit 9;
+c1
+test_test.t3
+handler h2 read first limit 9;
+c1
+t1
+handler test.h1 close;
+ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'close' at line 1
+handler t1 close;
+handler h1 close;
+handler h2 close;
+handler t1 read first limit 9;
+ERROR 42S02: Unknown table 't1' in HANDLER
+handler h1 read first limit 9;
+ERROR 42S02: Unknown table 'h1' in HANDLER
+handler h2 read first limit 9;
+ERROR 42S02: Unknown table 'h2' in HANDLER
+handler h3 read first limit 9;
+c1
+test_test.t3
+handler h3 read first limit 9;
+c1
+test_test.t3
+use test_test;
+handler h3 read first limit 9;
+c1
+test_test.t3
+handler test.h3 read first limit 9;
+ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'read first limit 9' at line 1
+handler h3 close;
+use test;
+drop table t3;
+drop table t2;
+drop table t1;
+drop database test_test;
+create table t1 (c1 int);
+create table t2 (c1 int);
+insert into t1 values (1);
+insert into t2 values (2);
+connection: default
+handler t1 open;
+handler t1 read first;
+c1
+1
+connection: flush
+flush tables;;
+connection: default
+handler t2 open;
+handler t2 read first;
+c1
+2
+handler t1 read next;
+c1
+1
+handler t1 close;
+handler t2 close;
+drop table t1,t2;
+create table t1 (a int);
+handler t1 open as t1_alias;
+drop table t1;
+create table t1 (a int);
+handler t1 open as t1_alias;
+flush tables;
+drop table t1;
+create table t1 (a int);
+handler t1 open as t1_alias;
+handler t1_alias close;
+drop table t1;
+create table t1 (a int);
+handler t1 open as t1_alias;
+handler t1_alias read first;
+a
+drop table t1;
+handler t1_alias read next;
+ERROR 42S02: Unknown table 't1_alias' in HANDLER
+create table t1 (c1 int);
+connection: default
+handler t1 open;
+handler t1 read first;
+c1
+connection: flush
+rename table t1 to t2;;
+connection: default
+handler t2 open;
+handler t2 read first;
+c1
+handler t1 read next;
+ERROR 42S02: Table 'test.t1' doesn't exist
+handler t1 close;
+handler t2 close;
+drop table t2;
+create table t1 (a int, b char(1), key a (a), key b (a,b));
+insert into t1 values (0,"a"),(1,"b"),(2,"c"),(3,"d"),(4,"e"),
+(5,"f"),(6,"g"),(7,"h"),(8,"i"),(9,"j");
+handler t1 open;
+handler t1 read a first;
+a b
+0 a
+handler t1 read a next;
+a b
+1 b
+flush tables;
+handler t1 read a next;
+a b
+0 a
+handler t1 read a next;
+a b
+1 b
+flush tables with read lock;
+handler t1 read a next;
+a b
+0 a
+unlock tables;
+drop table t1;
+handler t1 read a next;
+ERROR 42S02: Unknown table 't1' in HANDLER
+drop table if exists t1;
+create table t1 (a int not null);
+insert into t1 values (1);
+handler t1 open;
+alter table t1 engine=csv;
+handler t1 read a next;
+ERROR HY000: Table storage engine for 't1' doesn't have this option
+handler t1 close;
+drop table t1;
+USE information_schema;
+HANDLER COLUMNS OPEN;
+ERROR HY000: Incorrect usage of HANDLER OPEN and information_schema
+PREPARE h_r FROM 'HANDLER t1 READ `PRIMARY` LAST';
+ERROR 42S02: Unknown table 't1' in HANDLER
diff --git a/mysql-test/suite/handler/interface.test b/mysql-test/suite/handler/interface.test
new file mode 100644
index 00000000000..e03f2ed777c
--- /dev/null
+++ b/mysql-test/suite/handler/interface.test
@@ -0,0 +1,313 @@
+#
+# Tests of handler interface that are system independent
+#
+# Handler tests don't work yet with embedded server
+#
+-- source include/not_embedded.inc
+
+--disable_warnings
+drop table if exists t1,t3,t4,t5;
+drop database if exists test_test;
+--enable_warnings
+
+# Run tests with myisam (any engine should be ok)
+
+let $engine_type= MyISAM;
+
+--source init.inc
+
+#
+# Do some syntax checking
+#
+
+handler t1 open;
+--error ER_PARSE_ERROR
+handler t1 read a=(SELECT 1);
+--error ER_PARSE_ERROR
+handler t1 read a=(1) FIRST;
+--error ER_PARSE_ERROR
+handler t1 read a=(1) NEXT;
+--error ER_PARSE_ERROR
+handler t1 read last;
+handler t1 close;
+drop table t1;
+
+CREATE TABLE t1(a INT, PRIMARY KEY(a));
+insert into t1 values(1),(2);
+handler t1 open;
+--error ER_PARSE_ERROR
+handler t1 read primary=(1);
+handler t1 read `primary`=(1);
+handler t1 close;
+drop table t1;
+
+#
+# Check if two database names beginning the same are seen as different.
+#
+# This database begins like the usual 'test' database.
+#
+create database test_test;
+use test_test;
+eval create table t1(table_id char(20), primary key $key_type (table_id));
+insert into t1 values ('test_test.t1');
+insert into t1 values ('');
+handler t1 open;
+handler t1 read first limit 9;
+eval create table t2(table_id char(20), primary key $key_type (table_id));
+insert into t2 values ('test_test.t2');
+insert into t2 values ('');
+handler t2 open;
+handler t2 read first limit 9;
+#
+# This is the usual 'test' database.
+#
+use test;
+eval create table t1(table_id char(20), primary key $key_type (table_id));
+insert into t1 values ('test.t1');
+insert into t1 values ('');
+--error 1066
+handler t1 open;
+#
+# Check accessibility of all the tables.
+#
+use test;
+--error 1064
+handler test.t1 read first limit 9;
+--error 1064
+handler test_test.t1 read first limit 9;
+handler t1 read first limit 9;
+--error 1064
+handler test_test.t2 read first limit 9;
+handler t2 read first limit 9;
+
+#
+# Cleanup.
+#
+
+--error 1064
+handler test_test.t1 close;
+handler t1 close;
+drop table test_test.t1;
+--error 1064
+handler test_test.t2 close;
+handler t2 close;
+drop table test_test.t2;
+drop database test_test;
+
+#
+use test;
+--error 1064
+handler test.t1 close;
+--error 1109
+handler t1 close;
+drop table test.t1;
+
+#
+# BUG#4335 one name can be handler open'ed many times
+#
+
+create database test_test;
+use test_test;
+create table t1 (c1 char(20));
+insert into t1 values ('test_test.t1');
+create table t3 (c1 char(20));
+insert into t3 values ('test_test.t3');
+handler t1 open;
+handler t1 read first limit 9;
+handler t1 open h1;
+handler h1 read first limit 9;
+use test;
+create table t1 (c1 char(20));
+create table t2 (c1 char(20));
+create table t3 (c1 char(20));
+insert into t1 values ('t1');
+insert into t2 values ('t2');
+insert into t3 values ('t3');
+--error 1066
+handler t1 open;
+--error 1066
+handler t2 open t1;
+--error 1066
+handler t3 open t1;
+handler t1 read first limit 9;
+--error 1064
+handler test.t1 close;
+--error 1066
+handler test.t1 open h1;
+--error 1066
+handler test_test.t1 open h1;
+handler test_test.t3 open h3;
+handler test.t1 open h2;
+handler t1 read first limit 9;
+handler h1 read first limit 9;
+handler h2 read first limit 9;
+handler h3 read first limit 9;
+handler h2 read first limit 9;
+--error 1064
+handler test.h1 close;
+handler t1 close;
+handler h1 close;
+handler h2 close;
+--error 1109
+handler t1 read first limit 9;
+--error 1109
+handler h1 read first limit 9;
+--error 1109
+handler h2 read first limit 9;
+handler h3 read first limit 9;
+handler h3 read first limit 9;
+use test_test;
+handler h3 read first limit 9;
+--error 1064
+handler test.h3 read first limit 9;
+handler h3 close;
+use test;
+drop table t3;
+drop table t2;
+drop table t1;
+drop database test_test;
+
+#
+# Bug#21587 FLUSH TABLES causes server crash when used with HANDLER statements
+#
+
+create table t1 (c1 int);
+create table t2 (c1 int);
+insert into t1 values (1);
+insert into t2 values (2);
+--echo connection: default
+handler t1 open;
+handler t1 read first;
+connect (flush,localhost,root,,);
+connection flush;
+--echo connection: flush
+--send flush tables;
+connection default;
+--echo connection: default
+let $wait_condition=
+ select count(*) = 1 from information_schema.processlist
+ where state = "Flushing tables";
+--source include/wait_condition.inc
+handler t2 open;
+handler t2 read first;
+handler t1 read next;
+handler t1 close;
+handler t2 close;
+connection flush;
+reap;
+connection default;
+drop table t1,t2;
+disconnect flush;
+
+#
+# Bug#31397 Inconsistent drop table behavior of handler tables.
+#
+
+create table t1 (a int);
+handler t1 open as t1_alias;
+drop table t1;
+create table t1 (a int);
+handler t1 open as t1_alias;
+flush tables;
+drop table t1;
+create table t1 (a int);
+handler t1 open as t1_alias;
+handler t1_alias close;
+drop table t1;
+create table t1 (a int);
+handler t1 open as t1_alias;
+handler t1_alias read first;
+drop table t1;
+--error ER_UNKNOWN_TABLE
+handler t1_alias read next;
+
+#
+# Bug#31409 RENAME TABLE causes server crash or deadlock when used with
+# HANDLER statements
+#
+
+create table t1 (c1 int);
+--echo connection: default
+handler t1 open;
+handler t1 read first;
+connect (flush,localhost,root,,);
+connection flush;
+--echo connection: flush
+--send rename table t1 to t2;
+connection default;
+--echo connection: default
+let $wait_condition=
+ select count(*) = 1 from information_schema.processlist
+ where state = "Waiting for table" and info = "rename table t1 to t2";
+--source include/wait_condition.inc
+handler t2 open;
+handler t2 read first;
+--error ER_NO_SUCH_TABLE
+handler t1 read next;
+handler t1 close;
+handler t2 close;
+connection flush;
+reap;
+connection default;
+drop table t2;
+disconnect flush;
+
+# Flush tables causes handlers reopen
+
+eval create table t1 (a int, b char(1), key a $key_type (a), key b $key_type (a,b));
+insert into t1 values (0,"a"),(1,"b"),(2,"c"),(3,"d"),(4,"e"),
+ (5,"f"),(6,"g"),(7,"h"),(8,"i"),(9,"j");
+handler t1 open;
+handler t1 read a first;
+handler t1 read a next;
+flush tables;
+handler t1 read a next;
+handler t1 read a next;
+flush tables with read lock;
+handler t1 read a next;
+unlock tables;
+drop table t1;
+--error ER_UNKNOWN_TABLE
+handler t1 read a next;
+
+#
+# Bug#41110: crash with handler command when used concurrently with alter table
+# Bug#41112: crash in mysql_ha_close_table/get_lock_data with alter table
+#
+
+--disable_warnings
+drop table if exists t1;
+--enable_warnings
+create table t1 (a int not null);
+insert into t1 values (1);
+handler t1 open;
+connect(con1,localhost,root,,);
+send alter table t1 engine=csv;
+connection default;
+let $wait_condition=
+ select count(*) = 1 from information_schema.processlist
+ where state = "rename result table" and info = "alter table t1 engine=csv";
+--source include/wait_condition.inc
+--error ER_ILLEGAL_HA
+handler t1 read a next;
+handler t1 close;
+connection con1;
+--reap
+drop table t1;
+disconnect con1;
+--source include/wait_until_disconnected.inc
+connection default;
+
+#
+# Bug#44151 using handler commands on information_schema tables crashes server
+#
+
+USE information_schema;
+--error ER_WRONG_USAGE
+HANDLER COLUMNS OPEN;
+
+#
+# LP#697622 Assertion `! is_set()' failed when preparing a HANDLER statement
+#
+--error ER_UNKNOWN_TABLE
+PREPARE h_r FROM 'HANDLER t1 READ `PRIMARY` LAST';
diff --git a/mysql-test/r/handler_myisam.result b/mysql-test/suite/handler/myisam.result
index b20b8dbb138..fd333015bbe 100644
--- a/mysql-test/r/handler_myisam.result
+++ b/mysql-test/suite/handler/myisam.result
@@ -1,56 +1,53 @@
SET SESSION STORAGE_ENGINE = MyISAM;
drop table if exists t1,t3,t4,t5;
-create table t1 (a int, b char(10), key a(a), key b(a,b));
+create table t1 (a int, b char(10), key a (a), key b (a,b));
insert into t1 values
(17,"ddd"),(18,"eee"),(19,"fff"),(19,"yyy"),
-(14,"aaa"),(15,"bbb"),(16,"ccc"),(16,"xxx"),
+(14,"aaa"),(16,"ccc"),(16,"xxx"),
(20,"ggg"),(21,"hhh"),(22,"iii");
handler t1 open as t2;
-handler t2 read a=(SELECT 1);
-ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'SELECT 1)' at line 1
-handler t2 read a first;
+handler t2 read b first;
a b
14 aaa
-handler t2 read a next;
-a b
-15 bbb
-handler t2 read a next;
+handler t2 read b next;
a b
16 ccc
-handler t2 read a prev;
+handler t2 read b next;
a b
-15 bbb
-handler t2 read a last;
+16 xxx
+handler t2 read b prev;
+a b
+16 ccc
+handler t2 read b last;
a b
22 iii
-handler t2 read a prev;
+handler t2 read b prev;
a b
21 hhh
-handler t2 read a prev;
+handler t2 read b prev;
a b
20 ggg
-handler t2 read a first;
+handler t2 read b first;
a b
14 aaa
-handler t2 read a prev;
+handler t2 read b prev;
a b
-handler t2 read a last;
+handler t2 read b last;
a b
22 iii
-handler t2 read a prev;
+handler t2 read b prev;
a b
21 hhh
-handler t2 read a next;
+handler t2 read b next;
a b
22 iii
-handler t2 read a next;
+handler t2 read b next;
a b
handler t2 read a=(15);
a b
-15 bbb
-handler t2 read a=(16);
+handler t2 read a=(21);
a b
-16 ccc
+21 hhh
handler t2 read a=(19,"fff");
ERROR 42000: Too many key parts specified; max 1 parts allowed
handler t2 read b=(19,"fff");
@@ -69,49 +66,93 @@ a b
handler t2 read a>=(11);
a b
14 aaa
-handler t2 read a=(18);
+handler t2 read b=(18);
a b
18 eee
-handler t2 read a>=(18);
+handler t2 read b>=(18);
a b
18 eee
-handler t2 read a>(18);
+handler t2 read b>(18);
a b
19 fff
-handler t2 read a<=(18);
+handler t2 read b<=(18);
a b
18 eee
-handler t2 read a<(18);
+handler t2 read b<(18);
a b
17 ddd
-handler t2 read a first limit 5;
+handler t2 read a=(15);
+a b
+handler t2 read a>=(15) limit 2;
+a b
+16 ccc
+16 xxx
+handler t2 read a>(15) limit 2;
+a b
+16 ccc
+16 xxx
+handler t2 read a<=(15);
+a b
+14 aaa
+handler t2 read a<(15);
+a b
+14 aaa
+handler t2 read a=(54);
+a b
+handler t2 read a>=(54);
+a b
+handler t2 read a>(54);
+a b
+handler t2 read a<=(54);
+a b
+22 iii
+handler t2 read a<(54);
+a b
+22 iii
+handler t2 read a=(1);
+a b
+handler t2 read a>=(1);
+a b
+14 aaa
+handler t2 read a>(1);
+a b
+14 aaa
+handler t2 read a<=(1);
+a b
+handler t2 read a<(1);
+a b
+handler t2 read b first limit 5;
a b
14 aaa
-15 bbb
16 ccc
16 xxx
17 ddd
-handler t2 read a next limit 3;
-a b
18 eee
+handler t2 read b next limit 3;
+a b
19 fff
19 yyy
-handler t2 read a prev limit 10;
+20 ggg
+handler t2 read b prev limit 10;
a b
+19 yyy
19 fff
18 eee
17 ddd
16 xxx
16 ccc
-15 bbb
14 aaa
-handler t2 read a>=(16) limit 4;
+handler t2 read b>=(16) limit 4;
a b
16 ccc
16 xxx
17 ddd
18 eee
-handler t2 read a>=(16) limit 2,2;
+handler t2 read b>=(16) limit 2,2;
+a b
+17 ddd
+18 eee
+select * from t1 where a>=16 order by a,b limit 2,2;
a b
17 ddd
18 eee
@@ -120,10 +161,13 @@ a b
22 iii
21 hhh
20 ggg
-handler t2 read a=(19);
+handler t2 read b=(16) limit 1,3;
+a b
+16 xxx
+handler t2 read b=(19);
a b
19 fff
-handler t2 read a=(19) where b="yyy";
+handler t2 read b=(19) where b="yyy";
a b
19 yyy
handler t2 read first;
@@ -135,16 +179,14 @@ a b
handler t2 read next;
a b
19 fff
-handler t2 read last;
-ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '' at line 1
handler t2 close;
handler t1 open;
-handler t1 read a next;
+handler t1 read b next;
a b
14 aaa
-handler t1 read a next;
+handler t1 read b next;
a b
-15 bbb
+16 ccc
handler t1 close;
handler t1 open;
handler t1 read a prev;
@@ -161,14 +203,135 @@ a b
alter table t1 engine = MyISAM;
handler t2 read first;
ERROR 42S02: Unknown table 't2' in HANDLER
+handler t1 open;
+handler t1 read a=(16) limit 1,3;
+a b
+16 xxx
+flush tables;
+handler t1 read a=(16) limit 1,3;
+a b
+16 xxx
+handler t1 close;
+handler t1 open;
+prepare stmt from 'handler t1 read a=(?) limit ?,?';
+set @a=16,@b=1,@c=100;
+execute stmt using @a,@b,@c;
+a b
+16 xxx
+set @a=16,@b=2,@c=1;
+execute stmt using @a,@b,@c;
+a b
+set @a=16,@b=0,@c=2;
+execute stmt using @a,@b,@c;
+a b
+16 ccc
+16 xxx
+deallocate prepare stmt;
+prepare stmt from 'handler t1 read a next limit ?';
+handler t1 read a>=(11);
+a b
+14 aaa
+set @a=3;
+execute stmt using @a;
+a b
+16 ccc
+16 xxx
+17 ddd
+execute stmt using @a;
+a b
+18 eee
+19 fff
+19 yyy
+execute stmt using @a;
+a b
+20 ggg
+21 hhh
+22 iii
+deallocate prepare stmt;
+prepare stmt from 'handler t1 read b prev limit ?';
+execute stmt using @a;
+a b
+22 iii
+21 hhh
+20 ggg
+execute stmt using @a;
+a b
+19 yyy
+19 fff
+18 eee
+execute stmt using @a;
+a b
+17 ddd
+16 xxx
+16 ccc
+execute stmt using @a;
+a b
+14 aaa
+deallocate prepare stmt;
+prepare stmt from 'handler t1 read b=(?,?)';
+set @a=14, @b='aaa';
+execute stmt using @a,@b;
+a b
+14 aaa
+set @a=14, @b='not found';
+execute stmt using @a,@b;
+a b
+deallocate prepare stmt;
+prepare stmt from 'handler t1 read b=(1+?) limit 10';
+set @a=15;
+execute stmt using @a;
+a b
+16 ccc
+16 xxx
+execute stmt using @a;
+a b
+16 ccc
+16 xxx
+deallocate prepare stmt;
+prepare stmt from 'handler t1 read a>=(?) where a < ? limit 5';
+set @a=15, @b=20;
+execute stmt using @a,@b;
+a b
+16 ccc
+16 xxx
+17 ddd
+18 eee
+19 fff
+execute stmt using @a,@b;
+a b
+16 ccc
+16 xxx
+17 ddd
+18 eee
+19 fff
+deallocate prepare stmt;
+prepare stmt from 'handler t1 read a=(?)';
+set @a=16;
+execute stmt using @a;
+a b
+16 ccc
+alter table t1 add c int;
+execute stmt using @a;
+ERROR 42S02: Unknown table 't1' in HANDLER
+deallocate prepare stmt;
+handler t1 close;
+ERROR 42S02: Unknown table 't1' in HANDLER
+handler t1 open;
+prepare stmt from 'handler t1 read a=(?)';
+flush tables;
+set @a=16;
+execute stmt using @a;
+ERROR HY000: Prepared statement needs to be re-prepared
+deallocate prepare stmt;
+handler t1 close;
handler t1 open as t2;
drop table t1;
-create table t1 (a int);
+create table t1 (a int not null);
insert into t1 values (17);
handler t2 read first;
ERROR 42S02: Unknown table 't2' in HANDLER
handler t1 open as t2;
-alter table t1 engine=MEMORY;
+alter table t1 engine=CSV;
handler t2 read first;
ERROR 42S02: Unknown table 't2' in HANDLER
drop table t1;
@@ -191,7 +354,7 @@ handler t1 read first;
a
6
drop table t1;
-create table t1(a int, index(a));
+create table t1(a int, index (a));
insert into t1 values (1), (2), (3);
handler t1 open;
handler t1 read a=(W);
@@ -217,7 +380,7 @@ Ok
handler t close;
use test;
drop table t1;
-create table t1 ( a int, b int, INDEX a (a) );
+create table t1 ( a int, b int, INDEX a (a) );
insert into t1 values (1,2), (2,1);
handler t1 open;
handler t1 read a=(1) where b=2;
@@ -229,148 +392,6 @@ handler t1 read a=(1) where b=1;
a b
handler t1 close;
drop table t1;
-drop database if exists test_test;
-create database test_test;
-use test_test;
-create table t1(table_id char(20) primary key);
-insert into t1 values ('test_test.t1');
-insert into t1 values ('');
-handler t1 open;
-handler t1 read first limit 9;
-table_id
-test_test.t1
-
-create table t2(table_id char(20) primary key);
-insert into t2 values ('test_test.t2');
-insert into t2 values ('');
-handler t2 open;
-handler t2 read first limit 9;
-table_id
-test_test.t2
-
-use test;
-drop table if exists t1;
-create table t1(table_id char(20) primary key);
-insert into t1 values ('test.t1');
-insert into t1 values ('');
-handler t1 open;
-ERROR 42000: Not unique table/alias: 't1'
-use test;
-handler test.t1 read first limit 9;
-ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'read first limit 9' at line 1
-handler test_test.t1 read first limit 9;
-ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'read first limit 9' at line 1
-handler t1 read first limit 9;
-table_id
-test_test.t1
-
-handler test_test.t2 read first limit 9;
-ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'read first limit 9' at line 1
-handler t2 read first limit 9;
-table_id
-test_test.t2
-
-handler test_test.t1 close;
-ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'close' at line 1
-handler t1 close;
-drop table test_test.t1;
-handler test_test.t2 close;
-ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'close' at line 1
-handler t2 close;
-drop table test_test.t2;
-drop database test_test;
-use test;
-handler test.t1 close;
-ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'close' at line 1
-handler t1 close;
-ERROR 42S02: Unknown table 't1' in HANDLER
-drop table test.t1;
-drop database if exists test_test;
-drop table if exists t1;
-drop table if exists t2;
-drop table if exists t3;
-create database test_test;
-use test_test;
-create table t1 (c1 char(20));
-insert into t1 values ('test_test.t1');
-create table t3 (c1 char(20));
-insert into t3 values ('test_test.t3');
-handler t1 open;
-handler t1 read first limit 9;
-c1
-test_test.t1
-handler t1 open h1;
-handler h1 read first limit 9;
-c1
-test_test.t1
-use test;
-create table t1 (c1 char(20));
-create table t2 (c1 char(20));
-create table t3 (c1 char(20));
-insert into t1 values ('t1');
-insert into t2 values ('t2');
-insert into t3 values ('t3');
-handler t1 open;
-ERROR 42000: Not unique table/alias: 't1'
-handler t2 open t1;
-ERROR 42000: Not unique table/alias: 't1'
-handler t3 open t1;
-ERROR 42000: Not unique table/alias: 't1'
-handler t1 read first limit 9;
-c1
-test_test.t1
-handler test.t1 close;
-ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'close' at line 1
-handler test.t1 open h1;
-ERROR 42000: Not unique table/alias: 'h1'
-handler test_test.t1 open h1;
-ERROR 42000: Not unique table/alias: 'h1'
-handler test_test.t3 open h3;
-handler test.t1 open h2;
-handler t1 read first limit 9;
-c1
-test_test.t1
-handler h1 read first limit 9;
-c1
-test_test.t1
-handler h2 read first limit 9;
-c1
-t1
-handler h3 read first limit 9;
-c1
-test_test.t3
-handler h2 read first limit 9;
-c1
-t1
-handler test.h1 close;
-ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'close' at line 1
-handler t1 close;
-handler h1 close;
-handler h2 close;
-handler t1 read first limit 9;
-ERROR 42S02: Unknown table 't1' in HANDLER
-handler h1 read first limit 9;
-ERROR 42S02: Unknown table 'h1' in HANDLER
-handler h2 read first limit 9;
-ERROR 42S02: Unknown table 'h2' in HANDLER
-handler h3 read first limit 9;
-c1
-test_test.t3
-handler h3 read first limit 9;
-c1
-test_test.t3
-use test_test;
-handler h3 read first limit 9;
-c1
-test_test.t3
-handler test.h3 read first limit 9;
-ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'read first limit 9' at line 1
-handler h3 close;
-use test;
-drop table t3;
-drop table t2;
-drop table t1;
-drop database test_test;
create table t1 (c1 char(20));
insert into t1 values ("t1");
handler t1 open as h1;
@@ -495,6 +516,11 @@ no1 no2
HANDLER t1 READ `primary` PREV;
no1 no2
1 275
+HANDLER t1 READ `primary` = (1, 1000);
+no1 no2
+HANDLER t1 READ `primary` NEXT;
+no1 no2
+2 6
DROP TABLE t1;
create table t1 (c1 int);
insert into t1 values (14397);
@@ -515,14 +541,12 @@ ERROR 42S02: Table 'test.t1' doesn't exist
drop table if exists t1;
Warnings:
Note 1051 Unknown table 't1'
-drop table if exists t1;
-create table t1 (a int) ENGINE=MEMORY;
+create table t1 (a int not null) ENGINE=CSV;
--> client 2
handler t1 open;
ERROR HY000: Table storage engine for 't1' doesn't have this option
--> client 1
drop table t1;
-drop table if exists t1;
create table t1 (a int);
handler t1 open as t1_alias;
handler t1_alias read a next;
@@ -535,50 +559,9 @@ handler t1_alias READ a next where inexistent > 0;
ERROR 42S22: Unknown column 'inexistent' in 'field list'
handler t1_alias close;
drop table t1;
-drop table if exists t1,t2;
-create table t1 (c1 int);
-create table t2 (c1 int);
-insert into t1 values (1);
-insert into t2 values (2);
-connection: default
-handler t1 open;
-handler t1 read first;
-c1
-1
-connection: flush
-flush tables;;
-connection: default
-handler t2 open;
-handler t2 read first;
-c1
-2
-handler t1 read next;
-c1
-1
-handler t1 close;
-handler t2 close;
-drop table t1,t2;
-drop table if exists t1,t2;
-create table t1 (c1 int);
-connection: default
-handler t1 open;
-handler t1 read first;
-c1
-connection: flush
-rename table t1 to t2;;
-connection: default
-handler t2 open;
-handler t2 read first;
-c1
-handler t1 read next;
-ERROR 42S02: Table 'test.t1' doesn't exist
-handler t1 close;
-handler t2 close;
-drop table t2;
-drop table if exists t1;
-create temporary table t1 (a int, b char(1), key a(a), key b(a,b));
+create temporary table t1 (a int, b char(1), key a (a), key b (a,b));
insert into t1 values (0,"a"),(1,"b"),(2,"c"),(3,"d"),(4,"e"),
-(5,"f"),(6,"g"),(7,"h"),(8,"i"),(9,"j");
+(5,"f"),(6,"g"),(7,"h"),(8,"i"),(9,"j"),(9,'k');
select a,b from t1;
a b
0 a
@@ -591,24 +574,25 @@ a b
7 h
8 i
9 j
+9 k
handler t1 open as a1;
-handler a1 read a first;
-a b
-0 a
-handler a1 read a next;
+handler a1 read a=(1);
a b
1 b
handler a1 read a next;
a b
2 c
+handler a1 read a next;
+a b
+3 d
select a,b from t1;
ERROR HY000: Can't reopen table: 'a1'
handler a1 read a prev;
a b
-1 b
+2 c
handler a1 read a prev;
a b
-0 a
+1 b
handler a1 read a=(6) where b="g";
a b
6 g
@@ -625,39 +609,28 @@ a b
7 h
8 i
9 j
+9 k
handler t1 open as a2;
-handler a2 read a first;
-a b
-0 a
-handler a2 read a last;
+handler a2 read b=(9);
a b
9 j
-handler a2 read a prev;
+handler a2 read b next;
+a b
+9 k
+handler a2 read b prev limit 2;
a b
+9 j
8 i
+handler a2 read b last;
+a b
+9 k
+handler a2 read b prev;
+a b
+9 j
handler a2 close;
drop table t1;
-drop table if exists t1,t2;
-create table t1 (a int);
-handler t1 open as t1_alias;
-drop table t1;
-create table t1 (a int);
-handler t1 open as t1_alias;
-flush tables;
-drop table t1;
create table t1 (a int);
-handler t1 open as t1_alias;
-handler t1_alias close;
-drop table t1;
-create table t1 (a int);
-handler t1 open as t1_alias;
-handler t1_alias read first;
-a
-drop table t1;
-handler t1_alias read next;
-ERROR 42S02: Unknown table 't1_alias' in HANDLER
-create table t1 (a int);
-create temporary table t2 (a int, key(a));
+create temporary table t2 (a int, key (a));
handler t1 open as a1;
handler t2 open as a2;
handler a2 read a first;
@@ -667,7 +640,7 @@ handler a2 read a next;
ERROR 42S02: Unknown table 'a2' in HANDLER
handler a1 close;
ERROR 42S02: Unknown table 'a1' in HANDLER
-create table t1 (a int, key(a));
+create table t1 (a int, key (a));
create table t2 like t1;
handler t1 open as a1;
handler t2 open as a2;
@@ -680,7 +653,7 @@ handler a1 close;
ERROR 42S02: Unknown table 'a1' in HANDLER
handler a2 close;
drop table t1, t2;
-create table t1 (a int, key(a));
+create table t1 (a int, key (a));
handler t1 open as a1;
handler a1 read a first;
a
@@ -688,7 +661,7 @@ rename table t1 to t2;
handler a1 read a first;
ERROR 42S02: Unknown table 'a1' in HANDLER
drop table t2;
-create table t1 (a int, key(a));
+create table t1 (a int, key (a));
create table t2 like t1;
handler t1 open as a1;
handler t2 open as a2;
@@ -703,44 +676,19 @@ handler a1 close;
ERROR 42S02: Unknown table 'a1' in HANDLER
handler a2 close;
drop table t1, t2;
-create table t1 (a int, b char(1), key a(a), key b(a,b));
-insert into t1 values (0,"a"),(1,"b"),(2,"c"),(3,"d"),(4,"e"),
-(5,"f"),(6,"g"),(7,"h"),(8,"i"),(9,"j");
-handler t1 open;
-handler t1 read a first;
-a b
-0 a
-handler t1 read a next;
-a b
-1 b
-flush tables;
-handler t1 read a next;
-a b
-0 a
-handler t1 read a next;
-a b
-1 b
-flush tables with read lock;
-handler t1 read a next;
-a b
-0 a
-unlock tables;
-drop table t1;
-handler t1 read a next;
-ERROR 42S02: Unknown table 't1' in HANDLER
-drop table if exists t1;
-create table t1 (a int);
-insert into t1 values (1);
-handler t1 open;
-alter table t1 engine=memory;
-handler t1 read a next;
-ERROR HY000: Table storage engine for 't1' doesn't have this option
-handler t1 close;
-drop table t1;
-USE information_schema;
-HANDLER COLUMNS OPEN;
-ERROR HY000: Incorrect usage of HANDLER OPEN and information_schema
-USE test;
+#
+# BUG#51877 - HANDLER interface causes invalid memory read
+#
+CREATE TABLE t1(a INT, KEY (a));
+HANDLER t1 OPEN;
+HANDLER t1 READ a FIRST;
+a
+INSERT INTO t1 VALUES(1);
+HANDLER t1 READ a NEXT;
+a
+1
+HANDLER t1 CLOSE;
+DROP TABLE t1;
#
# BUG #46456: HANDLER OPEN + TRUNCATE + DROP (temporary) TABLE, crash
#
@@ -757,19 +705,6 @@ HANDLER t1 READ FIRST;
ERROR 42S02: Unknown table 't1' in HANDLER
DROP TABLE t1;
#
-# BUG#51877 - HANDLER interface causes invalid memory read
-#
-CREATE TABLE t1(a INT, KEY(a));
-HANDLER t1 OPEN;
-HANDLER t1 READ a FIRST;
-a
-INSERT INTO t1 VALUES(1);
-HANDLER t1 READ a NEXT;
-a
-1
-HANDLER t1 CLOSE;
-DROP TABLE t1;
-#
# Bug #54007: assert in ha_myisam::index_next , HANDLER
#
CREATE TABLE t1(a INT, b INT, PRIMARY KEY(a), KEY b(b), KEY ab(a, b));
@@ -821,12 +756,13 @@ HANDLER t1 READ b NEXT;
a b
HANDLER t1 READ NEXT;
a b
-4 40
+2 20
HANDLER t1 READ NEXT;
a b
-3 30
+1 10
HANDLER t1 READ NEXT;
a b
+4 40
HANDLER t1 CLOSE;
HANDLER t1 OPEN;
HANDLER t1 READ FIRST;
diff --git a/mysql-test/t/handler_myisam.test b/mysql-test/suite/handler/myisam.test
index e78072ef8a0..c6acf1e822c 100644
--- a/mysql-test/t/handler_myisam.test
+++ b/mysql-test/suite/handler/myisam.test
@@ -5,20 +5,14 @@
# Last update:
# 2006-07-31 ML test refactored (MySQL 5.1)
# code of t/handler.test and t/innodb_handler.test united
-# main testing code put into include/handler.inc
+# main testing code put into handler.inc
# rename t/handler.test to t/handler_myisam.test
#
-# should work in embedded server after mysqltest is fixed
---source include/not_embedded.inc
-
let $engine_type= MyISAM;
-let $other_engine_type= MEMORY;
-# There is unfortunately no other all time available storage engine
-# which supports the handler interface
-let $other_handler_engine_type= MyISAM;
---source include/handler.inc
+--source init.inc
+--source handler.inc
--echo #
--echo # BUG #46456: HANDLER OPEN + TRUNCATE + DROP (temporary) TABLE, crash
@@ -38,18 +32,6 @@ HANDLER t1 READ FIRST;
DROP TABLE t1;
--echo #
---echo # BUG#51877 - HANDLER interface causes invalid memory read
---echo #
-CREATE TABLE t1(a INT, KEY(a));
-HANDLER t1 OPEN;
-HANDLER t1 READ a FIRST;
-INSERT INTO t1 VALUES(1);
-HANDLER t1 READ a NEXT;
-HANDLER t1 CLOSE;
-DROP TABLE t1;
-
-
---echo #
--echo # Bug #54007: assert in ha_myisam::index_next , HANDLER
--echo #
CREATE TABLE t1(a INT, b INT, PRIMARY KEY(a), KEY b(b), KEY ab(a, b));
diff --git a/mysql-test/suite/innodb/r/innodb_mysql.result b/mysql-test/suite/innodb/r/innodb_mysql.result
index 8f876294039..fd9f794a8c0 100644
--- a/mysql-test/suite/innodb/r/innodb_mysql.result
+++ b/mysql-test/suite/innodb/r/innodb_mysql.result
@@ -2617,6 +2617,14 @@ ref NULL
rows 3
Extra Using index
DROP TABLE t1;
+#
+# ALTER TABLE IGNORE didn't ignore duplicates for unique add index
+#
+create table t1 (a int primary key, b int) engine = innodb;
+insert into t1 values (1,1),(2,1);
+alter ignore table t1 add unique `main` (b);
+drop table t1;
+#
End of 5.1 tests
#
# Test for bug #39932 "create table fails if column for FK is in different
diff --git a/mysql-test/suite/innodb/t/innodb_mysql.test b/mysql-test/suite/innodb/t/innodb_mysql.test
index 1b626297269..cec4085e9a5 100644
--- a/mysql-test/suite/innodb/t/innodb_mysql.test
+++ b/mysql-test/suite/innodb/t/innodb_mysql.test
@@ -840,6 +840,17 @@ CREATE INDEX b ON t1(a,b,c,d);
DROP TABLE t1;
+--echo #
+--echo # ALTER TABLE IGNORE didn't ignore duplicates for unique add index
+--echo #
+
+create table t1 (a int primary key, b int) engine = innodb;
+insert into t1 values (1,1),(2,1);
+alter ignore table t1 add unique `main` (b);
+drop table t1;
+
+--echo #
+
--echo End of 5.1 tests
--echo #
diff --git a/mysql-test/suite/maria/r/maria.result b/mysql-test/suite/maria/r/maria.result
index 2463bba1db4..0e58cc3b65d 100644
--- a/mysql-test/suite/maria/r/maria.result
+++ b/mysql-test/suite/maria/r/maria.result
@@ -1,6 +1,6 @@
select * from INFORMATION_SCHEMA.ENGINES where ENGINE="ARIA";
ENGINE SUPPORT COMMENT TRANSACTIONS XA SAVEPOINTS
-Aria YES Crash-safe tables with MyISAM heritage YES NO NO
+Aria YES Crash-safe tables with MyISAM heritage NO NO NO
set global storage_engine=aria;
set session storage_engine=aria;
set global aria_page_checksum=0;
@@ -2624,3 +2624,19 @@ KEY (v3)
INSERT INTO t1 ( f1 , f2 , f3 , f4 ) SELECT f1 , f4 , f1 , f4 FROM t1;
DELETE FROM t1;
drop table t1;
+create table t1 (a int not null primary key, b blob) engine=maria transactional=1;
+insert into t1 values(1,repeat('a',8000));
+insert into t1 values(2,repeat('b',8000));
+insert into t1 values(3,repeat('c',8000));
+flush tables;
+delete from t1 where a>1;
+insert into t1 values(1,repeat('d',8000*3));
+ERROR 23000: Duplicate entry '1' for key 'PRIMARY'
+flush tables;
+check table t1;
+Table Op Msg_type Msg_text
+test.t1 check status OK
+repair table t1 extended;
+Table Op Msg_type Msg_text
+test.t1 repair status OK
+drop table t1;
diff --git a/mysql-test/suite/maria/r/maria3.result b/mysql-test/suite/maria/r/maria3.result
index 2311669640b..490059587ad 100644
--- a/mysql-test/suite/maria/r/maria3.result
+++ b/mysql-test/suite/maria/r/maria3.result
@@ -1,6 +1,6 @@
select * from INFORMATION_SCHEMA.ENGINES where ENGINE="ARIA";
ENGINE SUPPORT COMMENT TRANSACTIONS XA SAVEPOINTS
-Aria YES Crash-safe tables with MyISAM heritage YES NO NO
+Aria YES Crash-safe tables with MyISAM heritage NO NO NO
set global storage_engine=aria;
set session storage_engine=aria;
set global aria_page_checksum=0;
diff --git a/mysql-test/suite/maria/t/maria-recovery-rtree-ft.test b/mysql-test/suite/maria/t/maria-recovery-rtree-ft.test
index a943185d12f..01ab5740258 100644
--- a/mysql-test/suite/maria/t/maria-recovery-rtree-ft.test
+++ b/mysql-test/suite/maria/t/maria-recovery-rtree-ft.test
@@ -6,6 +6,7 @@
# Binary must be compiled with debug for crash to occur
--source include/have_debug.inc
--source include/have_maria.inc
+--source include/long_test.inc
set global aria_log_file_size=4294967295;
let $MARIA_LOG=.;
diff --git a/mysql-test/suite/maria/t/maria.test b/mysql-test/suite/maria/t/maria.test
index ccbe2c99037..71d9dcec643 100644
--- a/mysql-test/suite/maria/t/maria.test
+++ b/mysql-test/suite/maria/t/maria.test
@@ -1911,6 +1911,24 @@ DELETE FROM t1;
drop table t1;
#
+# Test of problem where REPAIR finds old deleted rows.
+#
+
+create table t1 (a int not null primary key, b blob) engine=maria transactional=1;
+insert into t1 values(1,repeat('a',8000));
+insert into t1 values(2,repeat('b',8000));
+insert into t1 values(3,repeat('c',8000));
+flush tables;
+delete from t1 where a>1;
+--error 1062
+insert into t1 values(1,repeat('d',8000*3));
+flush tables;
+check table t1;
+# This failed by finding 2 extra rows.
+repair table t1 extended;
+drop table t1;
+
+#
# End of test
#
# Set defaults back
diff --git a/mysql-test/suite/parts/t/partition_alter2_1_myisam.test b/mysql-test/suite/parts/t/partition_alter2_1_myisam.test
index 1c89a82b960..11ec9b51f7c 100644
--- a/mysql-test/suite/parts/t/partition_alter2_1_myisam.test
+++ b/mysql-test/suite/parts/t/partition_alter2_1_myisam.test
@@ -22,6 +22,8 @@
# any of the variables.
#
+--source include/long_test.inc
+
#------------------------------------------------------------------------------#
# General not engine specific settings and requirements
diff --git a/mysql-test/suite/parts/t/partition_alter2_2_myisam.test b/mysql-test/suite/parts/t/partition_alter2_2_myisam.test
index c9b22ed8595..8fbb943a48d 100644
--- a/mysql-test/suite/parts/t/partition_alter2_2_myisam.test
+++ b/mysql-test/suite/parts/t/partition_alter2_2_myisam.test
@@ -22,6 +22,8 @@
# any of the variables.
#
+--source include/long_test.inc
+
#------------------------------------------------------------------------------#
# General not engine specific settings and requirements
diff --git a/mysql-test/suite/parts/t/partition_basic_innodb.test b/mysql-test/suite/parts/t/partition_basic_innodb.test
index 2fa94cbde21..8240257f087 100644
--- a/mysql-test/suite/parts/t/partition_basic_innodb.test
+++ b/mysql-test/suite/parts/t/partition_basic_innodb.test
@@ -22,6 +22,8 @@
# any of the variables.
#
+--source include/long_test.inc
+
#------------------------------------------------------------------------------#
# General not engine specific settings and requirements
diff --git a/mysql-test/suite/parts/t/partition_decimal_myisam.test b/mysql-test/suite/parts/t/partition_decimal_myisam.test
index 49fc64cbd37..9808dc878f8 100644
--- a/mysql-test/suite/parts/t/partition_decimal_myisam.test
+++ b/mysql-test/suite/parts/t/partition_decimal_myisam.test
@@ -22,6 +22,8 @@
# any of the variables.
#
+--source include/long_test.inc
+
#------------------------------------------------------------------------------#
# General not engine specific settings and requirements
diff --git a/mysql-test/suite/parts/t/partition_float_myisam.test b/mysql-test/suite/parts/t/partition_float_myisam.test
index 51e0f1f5a21..f15e6ad3636 100644
--- a/mysql-test/suite/parts/t/partition_float_myisam.test
+++ b/mysql-test/suite/parts/t/partition_float_myisam.test
@@ -22,6 +22,8 @@
# any of the variables.
#
+--source include/long_test.inc
+
#------------------------------------------------------------------------------#
# General not engine specific settings and requirements
diff --git a/mysql-test/suite/parts/t/partition_int_myisam.test b/mysql-test/suite/parts/t/partition_int_myisam.test
index b0ede4995e8..5f29b575244 100644
--- a/mysql-test/suite/parts/t/partition_int_myisam.test
+++ b/mysql-test/suite/parts/t/partition_int_myisam.test
@@ -22,6 +22,8 @@
# any of the variables.
#
+--source include/long_test.inc
+
#------------------------------------------------------------------------------#
# General not engine specific settings and requirements
diff --git a/mysql-test/suite/pbxt/t/lock_multi.test b/mysql-test/suite/pbxt/t/lock_multi.test
index 1104620b6b5..f90e31699c8 100644
--- a/mysql-test/suite/pbxt/t/lock_multi.test
+++ b/mysql-test/suite/pbxt/t/lock_multi.test
@@ -48,7 +48,7 @@ insert t1 select * from t2;
connection locker;
let $wait_condition=
select count(*) = 1 from information_schema.processlist
- where state = "Locked" and info = "insert t1 select * from t2";
+ where state = "Table Lock" and info = "insert t1 select * from t2";
--source include/wait_condition.inc
drop table t2;
connection reader;
@@ -72,7 +72,7 @@ connection locker;
# Sleep a bit till the insert of connection reader is in work and hangs
let $wait_condition=
select count(*) = 1 from information_schema.processlist
- where state = "Locked" and info = "insert t1 select * from t2";
+ where state = "Table Lock" and info = "insert t1 select * from t2";
--source include/wait_condition.inc
drop table t2;
connection reader;
@@ -251,7 +251,7 @@ connection reader;
# Wait till connection writer is blocked
let $wait_condition=
select count(*) = 1 from information_schema.processlist
- where state = "Locked" and info = "alter table t1 auto_increment=0";
+ where state = "Table Lock" and info = "alter table t1 auto_increment=0";
--source include/wait_condition.inc
send
alter table t1 auto_increment=0;
@@ -259,7 +259,7 @@ connection locker;
# Wait till connection reader is blocked
let $wait_condition=
select count(*) = 2 from information_schema.processlist
- where state = "Locked" and info = "alter table t1 auto_increment=0";
+ where state = "Table Lock" and info = "alter table t1 auto_increment=0";
--source include/wait_condition.inc
unlock tables;
connection writer;
@@ -414,16 +414,16 @@ update t1 set i= 10;
connection reader;
let $wait_condition=
select count(*) = 1 from information_schema.processlist
- where state = "Locked" and info = "update t1 set i= 10";
+ where state = "Table Lock" and info = "update t1 set i= 10";
--source include/wait_condition.inc
send
select * from t1;
connection default;
let $wait_condition=
select count(*) = 1 from information_schema.processlist
- where state = "Locked" and info = "select * from t1";
+ where state = "Table Lock" and info = "select * from t1";
--source include/wait_condition.inc
-let $ID= `select id from information_schema.processlist where state = "Locked" and info = "update t1 set i= 10"`;
+let $ID= `select id from information_schema.processlist where state = "Table Lock" and info = "update t1 set i= 10"`;
--replace_result $ID ID
eval kill query $ID;
connection reader;
@@ -557,7 +557,7 @@ connection waiter;
connection default;
let $wait_condition=
select count(*) = 1 from information_schema.processlist
- where state = "Locked" and info = "insert into t1 values(1)";
+ where state = "Table Lock" and info = "insert into t1 values(1)";
--source include/wait_condition.inc
let $tlwb= `show status like 'Table_locks_waited'`;
unlock tables;
diff --git a/mysql-test/suite/rpl/r/rpl_stop_slave.result b/mysql-test/suite/rpl/r/rpl_stop_slave.result
index 49146417ab7..4e16e8264f6 100644
--- a/mysql-test/suite/rpl/r/rpl_stop_slave.result
+++ b/mysql-test/suite/rpl/r/rpl_stop_slave.result
@@ -38,6 +38,7 @@ STOP SLAVE SQL_THREAD;
[ On Slave1 ]
# To resume slave SQL thread
SET DEBUG_SYNC= 'now SIGNAL signal.continue';
+SET DEBUG_SYNC= 'now WAIT_FOR signal.continued';
SET DEBUG_SYNC= 'RESET';
[ On Slave ]
@@ -63,6 +64,7 @@ STOP SLAVE SQL_THREAD;
[ On Slave1 ]
# To resume slave SQL thread
SET DEBUG_SYNC= 'now SIGNAL signal.continue';
+SET DEBUG_SYNC= 'now WAIT_FOR signal.continued';
SET DEBUG_SYNC= 'RESET';
[ On Slave ]
@@ -89,6 +91,7 @@ STOP SLAVE SQL_THREAD;
[ On Slave1 ]
# To resume slave SQL thread
SET DEBUG_SYNC= 'now SIGNAL signal.continue';
+SET DEBUG_SYNC= 'now WAIT_FOR signal.continued';
SET DEBUG_SYNC= 'RESET';
[ On Slave ]
@@ -115,6 +118,7 @@ STOP SLAVE SQL_THREAD;
[ On Slave1 ]
# To resume slave SQL thread
SET DEBUG_SYNC= 'now SIGNAL signal.continue';
+SET DEBUG_SYNC= 'now WAIT_FOR signal.continued';
SET DEBUG_SYNC= 'RESET';
[ On Slave ]
diff --git a/mysql-test/suite/rpl/t/rpl_deadlock_innodb.test b/mysql-test/suite/rpl/t/rpl_deadlock_innodb.test
index ee907f81b22..169ccbf7f18 100644
--- a/mysql-test/suite/rpl/t/rpl_deadlock_innodb.test
+++ b/mysql-test/suite/rpl/t/rpl_deadlock_innodb.test
@@ -7,5 +7,6 @@
########################################################
-- source include/not_ndb_default.inc
-- source include/have_innodb.inc
+-- source include/long_test.inc
let $engine_type=innodb;
-- source extra/rpl_tests/rpl_deadlock.test
diff --git a/mysql-test/suite/rpl/t/rpl_row_sp003.test b/mysql-test/suite/rpl/t/rpl_row_sp003.test
index ab49174ddfa..8ed47232ba9 100644
--- a/mysql-test/suite/rpl/t/rpl_row_sp003.test
+++ b/mysql-test/suite/rpl/t/rpl_row_sp003.test
@@ -10,6 +10,7 @@
-- source include/have_binlog_format_row.inc
# Slow test, don't run during staging part
-- source include/not_staging.inc
+--source include/long_test.inc
-- source include/master-slave.inc
let $engine_type=INNODB;
diff --git a/mysql-test/suite/sys_vars/r/concurrent_insert_func.result b/mysql-test/suite/sys_vars/r/concurrent_insert_func.result
index 774775a8287..64a14473216 100644
--- a/mysql-test/suite/sys_vars/r/concurrent_insert_func.result
+++ b/mysql-test/suite/sys_vars/r/concurrent_insert_func.result
@@ -37,9 +37,9 @@ INSERT INTO t1(name) VALUES('Record_7');
connection default;
## show processlist info and state ##
SELECT state,info FROM INFORMATION_SCHEMA.PROCESSLIST
-WHERE state= "Locked" AND info LIKE "INSERT INTO t1%";
+WHERE state= "Table Lock" AND info LIKE "INSERT INTO t1%";
state info
-Locked INSERT INTO t1(name) VALUES('Record_7')
+Table lock INSERT INTO t1(name) VALUES('Record_7')
## table contents befor UNLOCK ##
SELECT * FROM t1;
name
diff --git a/mysql-test/suite/sys_vars/t/concurrent_insert_func.test b/mysql-test/suite/sys_vars/t/concurrent_insert_func.test
index 1a600ffd7f6..f7bd7bce39a 100644
--- a/mysql-test/suite/sys_vars/t/concurrent_insert_func.test
+++ b/mysql-test/suite/sys_vars/t/concurrent_insert_func.test
@@ -98,12 +98,12 @@ INSERT INTO t1(name) VALUES('Record_7');
connection default;
# wait until INSERT will be locked (low performance)
let $wait_condition= SELECT COUNT(*) = 1 FROM INFORMATION_SCHEMA.PROCESSLIST
- WHERE state= "Locked" AND info LIKE "INSERT INTO t1%";
+ WHERE state= "Table Lock" AND info LIKE "INSERT INTO t1%";
--source include/wait_condition.inc
--echo ## show processlist info and state ##
SELECT state,info FROM INFORMATION_SCHEMA.PROCESSLIST
-WHERE state= "Locked" AND info LIKE "INSERT INTO t1%";
+WHERE state= "Table Lock" AND info LIKE "INSERT INTO t1%";
--echo ## table contents befor UNLOCK ##
SELECT * FROM t1;
UNLOCK TABLES;
diff --git a/mysql-test/suite/sys_vars/t/delayed_insert_limit_func.test b/mysql-test/suite/sys_vars/t/delayed_insert_limit_func.test
index 61f7d801a1a..3de8d432d1a 100644
--- a/mysql-test/suite/sys_vars/t/delayed_insert_limit_func.test
+++ b/mysql-test/suite/sys_vars/t/delayed_insert_limit_func.test
@@ -122,7 +122,7 @@ connection default;
--echo ** Wait till con0 is blocked **
let $wait_condition=
SELECT COUNT(*) = 1 FROM information_schema.processlist
- WHERE state = 'Locked' AND info = '$my_select';
+ WHERE state = "Table Lock" AND info = '$my_select';
--source include/wait_condition.inc
UNLOCK TABLES;
@@ -207,7 +207,7 @@ connection default;
--echo ** Wait till con0 is blocked **
let $wait_condition=
SELECT COUNT(*) = 1 FROM information_schema.processlist
- WHERE state = 'Locked' AND info = '$my_select';
+ WHERE state = "Table Lock" AND info = '$my_select';
--source include/wait_condition.inc
UNLOCK TABLES;
diff --git a/mysql-test/suite/sys_vars/t/query_cache_wlock_invalidate_func.test b/mysql-test/suite/sys_vars/t/query_cache_wlock_invalidate_func.test
index e5ced59d175..baa490986e2 100644
--- a/mysql-test/suite/sys_vars/t/query_cache_wlock_invalidate_func.test
+++ b/mysql-test/suite/sys_vars/t/query_cache_wlock_invalidate_func.test
@@ -139,7 +139,7 @@ send SELECT * FROM t1;
connection con0;
--echo wait until table is locked
-let $wait_condition= SELECT count(*) > 0 FROM information_schema.processlist WHERE state= 'Locked';
+let $wait_condition= SELECT count(*) > 0 FROM information_schema.processlist WHERE state= "Table Lock";
--source include/wait_condition.inc
UNLOCK TABLES;
diff --git a/mysql-test/suite/sys_vars/t/sql_low_priority_updates_func.test b/mysql-test/suite/sys_vars/t/sql_low_priority_updates_func.test
index 5e0314c25ae..6a4cb2b664c 100644
--- a/mysql-test/suite/sys_vars/t/sql_low_priority_updates_func.test
+++ b/mysql-test/suite/sys_vars/t/sql_low_priority_updates_func.test
@@ -85,7 +85,7 @@ delimiter ;|
--echo ** Connection con0 **
connection con0;
-let $wait_condition = SELECT COUNT(*) > 0 FROM information_schema.processlist WHERE state='Locked' AND info LIKE 'UPDATE t1 SET a = CONCAT(a,"-updated")';
+let $wait_condition = SELECT COUNT(*) > 0 FROM information_schema.processlist WHERE state="Table Lock" AND info LIKE 'UPDATE t1 SET a = CONCAT(a,"-updated")';
--source include/wait_condition.inc
--echo ** Asynchronous Execution **
@@ -101,7 +101,7 @@ delimiter ;|
--echo ** Connection default **
connection default;
-let $wait_condition= SELECT count(*) = 2 FROM information_schema.processlist WHERE state LIKE 'Locked';
+let $wait_condition= SELECT count(*) = 2 FROM information_schema.processlist WHERE state LIKE "Table Lock";
--source include/wait_condition.inc
UNLOCK TABLES;
@@ -156,7 +156,7 @@ delimiter ;|
--echo ** Connection con0 **
connection con0;
-let $wait_condition = SELECT COUNT(*) > 0 FROM information_schema.processlist WHERE state='Locked' AND info LIKE 'UPDATE t1 SET a = CONCAT(a,"-updated")';
+let $wait_condition = SELECT COUNT(*) > 0 FROM information_schema.processlist WHERE state="Table Lock" AND info LIKE 'UPDATE t1 SET a = CONCAT(a,"-updated")';
--source include/wait_condition.inc
--echo ** Asynchronous Execution **
@@ -172,7 +172,7 @@ delimiter ;|
--echo ** Connection default **
connection default;
-let $wait_condition= SELECT count(*) = 2 FROM information_schema.processlist WHERE state LIKE 'Locked';
+let $wait_condition= SELECT count(*) = 2 FROM information_schema.processlist WHERE state LIKE "Table Lock";
--source include/wait_condition.inc
UNLOCK TABLES;
diff --git a/mysql-test/suite/vcol/inc/vcol_unsupported_storage_engines.inc b/mysql-test/suite/vcol/inc/vcol_unsupported_storage_engines.inc
index 681ed77fb3c..4ec98ebf3f4 100644
--- a/mysql-test/suite/vcol/inc/vcol_unsupported_storage_engines.inc
+++ b/mysql-test/suite/vcol/inc/vcol_unsupported_storage_engines.inc
@@ -13,9 +13,9 @@
# Change: Syntax changed
################################################################################
---error ER_UNSUPPORTED_ACTION_ON_VIRTUAL_COLUMN
+--error ER_UNSUPPORTED_ENGINE_FOR_VIRTUAL_COLUMNS
create table t1 (a int, b int as (a+1));
-create table t1 (a int);
---error ER_UNSUPPORTED_ACTION_ON_VIRTUAL_COLUMN
+create table t1 (a int not null);
+--error ER_UNSUPPORTED_ENGINE_FOR_VIRTUAL_COLUMNS
alter table t1 add column b int as (a+1);
drop table t1;
diff --git a/mysql-test/suite/vcol/r/vcol_archive.result b/mysql-test/suite/vcol/r/vcol_archive.result
index 83fb78a5592..5ed2f3768ca 100644
--- a/mysql-test/suite/vcol/r/vcol_archive.result
+++ b/mysql-test/suite/vcol/r/vcol_archive.result
@@ -1,7 +1,7 @@
SET @@session.storage_engine = 'archive';
create table t1 (a int, b int as (a+1));
-ERROR HY000: 'Specified storage engine' is not yet supported for computed columns
-create table t1 (a int);
+ERROR HY000: ARCHIVE storage engine does not support computed columns
+create table t1 (a int not null);
alter table t1 add column b int as (a+1);
-ERROR HY000: 'Specified storage engine' is not yet supported for computed columns
+ERROR HY000: ARCHIVE storage engine does not support computed columns
drop table t1;
diff --git a/mysql-test/suite/vcol/r/vcol_blackhole.result b/mysql-test/suite/vcol/r/vcol_blackhole.result
index 15e7505aebb..2d33937a2f1 100644
--- a/mysql-test/suite/vcol/r/vcol_blackhole.result
+++ b/mysql-test/suite/vcol/r/vcol_blackhole.result
@@ -1,7 +1,7 @@
SET @@session.storage_engine = 'blackhole';
create table t1 (a int, b int as (a+1));
-ERROR HY000: 'Specified storage engine' is not yet supported for computed columns
-create table t1 (a int);
+ERROR HY000: BLACKHOLE storage engine does not support computed columns
+create table t1 (a int not null);
alter table t1 add column b int as (a+1);
-ERROR HY000: 'Specified storage engine' is not yet supported for computed columns
+ERROR HY000: BLACKHOLE storage engine does not support computed columns
drop table t1;
diff --git a/mysql-test/suite/vcol/r/vcol_csv.result b/mysql-test/suite/vcol/r/vcol_csv.result
index 97977505696..920e614c0b1 100644
--- a/mysql-test/suite/vcol/r/vcol_csv.result
+++ b/mysql-test/suite/vcol/r/vcol_csv.result
@@ -1,7 +1,7 @@
SET @@session.storage_engine = 'CSV';
create table t1 (a int, b int as (a+1));
-ERROR HY000: 'Specified storage engine' is not yet supported for computed columns
+ERROR HY000: CSV storage engine does not support computed columns
create table t1 (a int not null);
alter table t1 add column b int as (a+1);
-ERROR HY000: 'Specified storage engine' is not yet supported for computed columns
+ERROR HY000: CSV storage engine does not support computed columns
drop table t1;
diff --git a/mysql-test/suite/vcol/r/vcol_memory.result b/mysql-test/suite/vcol/r/vcol_memory.result
index 30b6bd4a4bf..4503c51e39a 100644
--- a/mysql-test/suite/vcol/r/vcol_memory.result
+++ b/mysql-test/suite/vcol/r/vcol_memory.result
@@ -1,7 +1,7 @@
SET @@session.storage_engine = 'memory';
create table t1 (a int, b int as (a+1));
-ERROR HY000: 'Specified storage engine' is not yet supported for computed columns
-create table t1 (a int);
+ERROR HY000: MEMORY storage engine does not support computed columns
+create table t1 (a int not null);
alter table t1 add column b int as (a+1);
-ERROR HY000: 'Specified storage engine' is not yet supported for computed columns
+ERROR HY000: MEMORY storage engine does not support computed columns
drop table t1;
diff --git a/mysql-test/suite/vcol/r/vcol_merge.result b/mysql-test/suite/vcol/r/vcol_merge.result
index 03a1e151c2e..4b5ed838c3a 100644
--- a/mysql-test/suite/vcol/r/vcol_merge.result
+++ b/mysql-test/suite/vcol/r/vcol_merge.result
@@ -4,5 +4,5 @@ create table t2 (a int, b int as (a % 10));
insert into t1 values (1,default);
insert into t2 values (2,default);
create table t3 (a int, b int as (a % 10)) engine=MERGE UNION=(t1,t2);
-ERROR HY000: 'Specified storage engine' is not yet supported for computed columns
+ERROR HY000: MRG_MYISAM storage engine does not support computed columns
drop table t1,t2;
diff --git a/mysql-test/suite/vcol/r/vcol_non_stored_columns_innodb.result b/mysql-test/suite/vcol/r/vcol_non_stored_columns_innodb.result
index c638ced4f41..0379a71922d 100644
--- a/mysql-test/suite/vcol/r/vcol_non_stored_columns_innodb.result
+++ b/mysql-test/suite/vcol/r/vcol_non_stored_columns_innodb.result
@@ -76,7 +76,7 @@ drop table t1;
# Case 7. ALTER. Modify virtual stored -> virtual non-stored
create table t1 (a int, b int as (a % 2) persistent);
alter table t1 modify b int as (a % 2);
-ERROR HY000: 'Changing the STORED status' is not yet supported for computed columns
+ERROR HY000: This is not yet supported for computed columns
show create table t1;
Table Create Table
t1 CREATE TABLE `t1` (
@@ -87,7 +87,7 @@ drop table t1;
# Case 8. ALTER. Modify virtual non-stored -> virtual stored
create table t1 (a int, b int as (a % 2));
alter table t1 modify b int as (a % 2) persistent;
-ERROR HY000: 'Changing the STORED status' is not yet supported for computed columns
+ERROR HY000: This is not yet supported for computed columns
show create table t1;
Table Create Table
t1 CREATE TABLE `t1` (
diff --git a/mysql-test/suite/vcol/r/vcol_non_stored_columns_myisam.result b/mysql-test/suite/vcol/r/vcol_non_stored_columns_myisam.result
index be42b8b76c4..de9a962ac2c 100644
--- a/mysql-test/suite/vcol/r/vcol_non_stored_columns_myisam.result
+++ b/mysql-test/suite/vcol/r/vcol_non_stored_columns_myisam.result
@@ -76,7 +76,7 @@ drop table t1;
# Case 7. ALTER. Modify virtual stored -> virtual non-stored
create table t1 (a int, b int as (a % 2) persistent);
alter table t1 modify b int as (a % 2);
-ERROR HY000: 'Changing the STORED status' is not yet supported for computed columns
+ERROR HY000: This is not yet supported for computed columns
show create table t1;
Table Create Table
t1 CREATE TABLE `t1` (
@@ -87,7 +87,7 @@ drop table t1;
# Case 8. ALTER. Modify virtual non-stored -> virtual stored
create table t1 (a int, b int as (a % 2));
alter table t1 modify b int as (a % 2) persistent;
-ERROR HY000: 'Changing the STORED status' is not yet supported for computed columns
+ERROR HY000: This is not yet supported for computed columns
show create table t1;
Table Create Table
t1 CREATE TABLE `t1` (
diff --git a/mysql-test/suite/vcol/t/rpl_vcol.test b/mysql-test/suite/vcol/t/rpl_vcol.test
index 2ac31f5ba35..df07b3aed2d 100644
--- a/mysql-test/suite/vcol/t/rpl_vcol.test
+++ b/mysql-test/suite/vcol/t/rpl_vcol.test
@@ -27,7 +27,7 @@
##### Storage engine to be tested
# Set the session storage engine
---source include/have_innodb.inc
+--source include/have_xtradb.inc
SET @@session.storage_engine = 'InnoDB';
#------------------------------------------------------------------------------#
diff --git a/mysql-test/suite/vcol/t/vcol_blocked_sql_funcs_innodb.test b/mysql-test/suite/vcol/t/vcol_blocked_sql_funcs_innodb.test
index 516e121a2aa..baefddc0fd1 100644
--- a/mysql-test/suite/vcol/t/vcol_blocked_sql_funcs_innodb.test
+++ b/mysql-test/suite/vcol/t/vcol_blocked_sql_funcs_innodb.test
@@ -32,7 +32,7 @@
##### Storage engine to be tested
# Set the session storage engine
---source include/have_innodb.inc
+--source include/have_xtradb.inc
eval SET @@session.storage_engine = 'InnoDB';
let $skip_full_text_checks = 1;
diff --git a/mysql-test/suite/vcol/t/vcol_column_def_options_innodb.test b/mysql-test/suite/vcol/t/vcol_column_def_options_innodb.test
index 38baa2b3024..e11618163cc 100644
--- a/mysql-test/suite/vcol/t/vcol_column_def_options_innodb.test
+++ b/mysql-test/suite/vcol/t/vcol_column_def_options_innodb.test
@@ -33,7 +33,7 @@
##### Storage engine to be tested
# Set the session storage engine
---source include/have_innodb.inc
+--source include/have_xtradb.inc
eval SET @@session.storage_engine = 'InnoDB';
##### Workarounds for known open engine specific bugs
diff --git a/mysql-test/suite/vcol/t/vcol_csv.test b/mysql-test/suite/vcol/t/vcol_csv.test
index b8342e24e07..75ddf819818 100644
--- a/mysql-test/suite/vcol/t/vcol_csv.test
+++ b/mysql-test/suite/vcol/t/vcol_csv.test
@@ -41,13 +41,7 @@ SET @@session.storage_engine = 'CSV';
# Execute the tests to be applied to all storage engines
#------------------------------------------------------------------------------#
-# Execute storage engine specific tests
---error ER_UNSUPPORTED_ACTION_ON_VIRTUAL_COLUMN
-create table t1 (a int, b int as (a+1));
-create table t1 (a int not null);
---error ER_UNSUPPORTED_ACTION_ON_VIRTUAL_COLUMN
-alter table t1 add column b int as (a+1);
-drop table t1;
+--source suite/vcol/inc/vcol_unsupported_storage_engines.inc
#------------------------------------------------------------------------------#
# Cleanup
diff --git a/mysql-test/suite/vcol/t/vcol_handler_innodb.test b/mysql-test/suite/vcol/t/vcol_handler_innodb.test
index bf443c6bbd3..1a50aeaaa86 100644
--- a/mysql-test/suite/vcol/t/vcol_handler_innodb.test
+++ b/mysql-test/suite/vcol/t/vcol_handler_innodb.test
@@ -33,7 +33,7 @@
##### Storage engine to be tested
# Set the session storage engine
---source include/have_innodb.inc
+--source include/have_xtradb.inc
eval SET @@session.storage_engine = 'InnoDB';
##### Workarounds for known open engine specific bugs
diff --git a/mysql-test/suite/vcol/t/vcol_ins_upd_innodb.test b/mysql-test/suite/vcol/t/vcol_ins_upd_innodb.test
index 5d9ac12e930..3b83c7f4565 100644
--- a/mysql-test/suite/vcol/t/vcol_ins_upd_innodb.test
+++ b/mysql-test/suite/vcol/t/vcol_ins_upd_innodb.test
@@ -33,7 +33,7 @@
##### Storage engine to be tested
# Set the session storage engine
---source include/have_innodb.inc
+--source include/have_xtradb.inc
eval SET @@session.storage_engine = 'InnoDB';
##### Workarounds for known open engine specific bugs
diff --git a/mysql-test/suite/vcol/t/vcol_keys_innodb.test b/mysql-test/suite/vcol/t/vcol_keys_innodb.test
index e408672ac07..d44d2f701cf 100644
--- a/mysql-test/suite/vcol/t/vcol_keys_innodb.test
+++ b/mysql-test/suite/vcol/t/vcol_keys_innodb.test
@@ -33,7 +33,7 @@
##### Storage engine to be tested
# Set the session storage engine
---source include/have_innodb.inc
+--source include/have_xtradb.inc
eval SET @@session.storage_engine = 'InnoDB';
##### Workarounds for known open engine specific bugs
diff --git a/mysql-test/suite/vcol/t/vcol_merge.test b/mysql-test/suite/vcol/t/vcol_merge.test
index 7ba72441bf5..a1d3c628c8e 100644
--- a/mysql-test/suite/vcol/t/vcol_merge.test
+++ b/mysql-test/suite/vcol/t/vcol_merge.test
@@ -48,7 +48,7 @@ create table t1 (a int, b int as (a % 10));
create table t2 (a int, b int as (a % 10));
insert into t1 values (1,default);
insert into t2 values (2,default);
---error ER_UNSUPPORTED_ACTION_ON_VIRTUAL_COLUMN
+--error ER_UNSUPPORTED_ENGINE_FOR_VIRTUAL_COLUMNS
create table t3 (a int, b int as (a % 10)) engine=MERGE UNION=(t1,t2);
drop table t1,t2;
diff --git a/mysql-test/suite/vcol/t/vcol_non_stored_columns_innodb.test b/mysql-test/suite/vcol/t/vcol_non_stored_columns_innodb.test
index 88ed6157294..42834d5c0bb 100644
--- a/mysql-test/suite/vcol/t/vcol_non_stored_columns_innodb.test
+++ b/mysql-test/suite/vcol/t/vcol_non_stored_columns_innodb.test
@@ -35,7 +35,7 @@
##### Storage engine to be tested
# Set the session storage engine
---source include/have_innodb.inc
+--source include/have_xtradb.inc
eval SET @@session.storage_engine = 'InnoDB';
##### Workarounds for known open engine specific bugs
diff --git a/mysql-test/suite/vcol/t/vcol_partition_innodb.test b/mysql-test/suite/vcol/t/vcol_partition_innodb.test
index 01230120ef9..47f1581fc96 100644
--- a/mysql-test/suite/vcol/t/vcol_partition_innodb.test
+++ b/mysql-test/suite/vcol/t/vcol_partition_innodb.test
@@ -34,7 +34,7 @@
##### Storage engine to be tested
# Set the session storage engine
---source include/have_innodb.inc
+--source include/have_xtradb.inc
eval SET @@session.storage_engine = 'InnoDB';
##### Workarounds for known open engine specific bugs
diff --git a/mysql-test/suite/vcol/t/vcol_select_innodb.test b/mysql-test/suite/vcol/t/vcol_select_innodb.test
index 314aecb75b9..787f5fe77a7 100644
--- a/mysql-test/suite/vcol/t/vcol_select_innodb.test
+++ b/mysql-test/suite/vcol/t/vcol_select_innodb.test
@@ -33,7 +33,7 @@
##### Storage engine to be tested
# Set the session storage engine
---source include/have_innodb.inc
+--source include/have_xtradb.inc
eval SET @@session.storage_engine = 'InnoDB';
##### Workarounds for known open engine specific bugs
diff --git a/mysql-test/suite/vcol/t/vcol_supported_sql_funcs_innodb.test b/mysql-test/suite/vcol/t/vcol_supported_sql_funcs_innodb.test
index 53826a460a7..32e2600c2fc 100644
--- a/mysql-test/suite/vcol/t/vcol_supported_sql_funcs_innodb.test
+++ b/mysql-test/suite/vcol/t/vcol_supported_sql_funcs_innodb.test
@@ -32,7 +32,7 @@
##### Storage engine to be tested
# Set the session storage engine
---source include/have_innodb.inc
+--source include/have_xtradb.inc
SET @@session.storage_engine = 'InnoDB';
##### Workarounds for known open engine specific bugs
diff --git a/mysql-test/suite/vcol/t/vcol_trigger_sp_innodb.test b/mysql-test/suite/vcol/t/vcol_trigger_sp_innodb.test
index 5a36fb1c06d..57655d6d3fe 100644
--- a/mysql-test/suite/vcol/t/vcol_trigger_sp_innodb.test
+++ b/mysql-test/suite/vcol/t/vcol_trigger_sp_innodb.test
@@ -34,7 +34,7 @@
##### Storage engine to be tested
# Set the session storage engine
---source include/have_innodb.inc
+--source include/have_xtradb.inc
eval SET @@session.storage_engine = 'InnoDB';
##### Workarounds for known open engine specific bugs
diff --git a/mysql-test/suite/vcol/t/vcol_view_innodb.test b/mysql-test/suite/vcol/t/vcol_view_innodb.test
index 01fced8e4c3..322fb122436 100644
--- a/mysql-test/suite/vcol/t/vcol_view_innodb.test
+++ b/mysql-test/suite/vcol/t/vcol_view_innodb.test
@@ -33,7 +33,7 @@
##### Storage engine to be tested
# Set the session storage engine
---source include/have_innodb.inc
+--source include/have_xtradb.inc
eval SET @@session.storage_engine = 'InnoDB';
##### Workarounds for known open engine specific bugs
diff --git a/mysql-test/t/handler_innodb.test b/mysql-test/t/handler_innodb.test
deleted file mode 100644
index 02982716f78..00000000000
--- a/mysql-test/t/handler_innodb.test
+++ /dev/null
@@ -1,20 +0,0 @@
-# t/handler_innodb.test
-#
-# test of HANDLER ...
-#
-# Last update:
-# 2006-07-31 ML test refactored (MySQL 5.1)
-# code of t/handler.test and t/innodb_handler.test united
-# main testing code put into include/handler.inc
-# rename t/innodb_handler.test to t/handler_innodb.test
-#
-
-# should work in embedded server after mysqltest is fixed
---source include/not_embedded.inc
-
---source include/have_innodb.inc
-let $engine_type= InnoDB;
-let $other_engine_type= MEMORY;
-let $other_handler_engine_type= MyISAM;
-
---source include/handler.inc
diff --git a/mysql-test/t/insert_notembedded.test b/mysql-test/t/insert_notembedded.test
index 24040f9c310..baf6a9a28a3 100644
--- a/mysql-test/t/insert_notembedded.test
+++ b/mysql-test/t/insert_notembedded.test
@@ -174,7 +174,7 @@ connection default;
# we must wait till the insert opens and locks the table
let $wait_condition=
select count(*) = 1 from information_schema.processlist
- where state = "Locked" and id = $ID;
+ where state = "Table lock" and id = $ID;
--source include/wait_condition.inc
connect (select,localhost,root,,);
--echo connection: select
diff --git a/mysql-test/t/join_outer.test b/mysql-test/t/join_outer.test
index 1322976a694..28526a30ae7 100644
--- a/mysql-test/t/join_outer.test
+++ b/mysql-test/t/join_outer.test
@@ -1,3 +1,5 @@
+--source include/long_test.inc
+
#
# test of left outer join
#
diff --git a/mysql-test/t/lock_multi.test b/mysql-test/t/lock_multi.test
index 4df1a0f3478..f05821af5a8 100644
--- a/mysql-test/t/lock_multi.test
+++ b/mysql-test/t/lock_multi.test
@@ -24,7 +24,7 @@ connection reader;
# Sleep a bit till the update of connection writer is in work and hangs
let $wait_condition=
select count(*) = 1 from information_schema.processlist
- where state = "Locked" and info = "update low_priority t1 set n = 4";
+ where state = "Table lock" and info = "update low_priority t1 set n = 4";
--source include/wait_condition.inc
send
select n from t1;
@@ -32,7 +32,7 @@ connection locker;
# Sleep a bit till the select of connection reader is in work and hangs
let $wait_condition=
select count(*) = 1 from information_schema.processlist
- where state = "Locked" and info = "select n from t1";
+ where state = "Table lock" and info = "select n from t1";
--source include/wait_condition.inc
unlock tables;
connection writer;
@@ -52,7 +52,7 @@ connection reader;
# Sleep a bit till the update of connection writer is in work and hangs
let $wait_condition=
select count(*) = 1 from information_schema.processlist
- where state = "Locked" and info = "update low_priority t1 set n = 4";
+ where state = "Table Lock" and info = "update low_priority t1 set n = 4";
--source include/wait_condition.inc
select n from t1;
connection locker;
@@ -96,7 +96,7 @@ insert t1 select * from t2;
connection locker;
let $wait_condition=
select count(*) = 1 from information_schema.processlist
- where state = "Locked" and info = "insert t1 select * from t2";
+ where state = "Table Lock" and info = "insert t1 select * from t2";
--source include/wait_condition.inc
drop table t2;
connection reader;
@@ -120,7 +120,7 @@ connection locker;
# Sleep a bit till the insert of connection reader is in work and hangs
let $wait_condition=
select count(*) = 1 from information_schema.processlist
- where state = "Locked" and info = "insert t1 select * from t2";
+ where state = "Table Lock" and info = "insert t1 select * from t2";
--source include/wait_condition.inc
drop table t2;
connection reader;
@@ -299,7 +299,7 @@ connection reader;
# Wait till connection writer is blocked
let $wait_condition=
select count(*) = 1 from information_schema.processlist
- where state = "Locked" and info = "alter table t1 auto_increment=0";
+ where state = "Table Lock" and info = "alter table t1 auto_increment=0";
--source include/wait_condition.inc
send
alter table t1 auto_increment=0;
@@ -307,7 +307,7 @@ connection locker;
# Wait till connection reader is blocked
let $wait_condition=
select count(*) = 2 from information_schema.processlist
- where state = "Locked" and info = "alter table t1 auto_increment=0";
+ where state = "Table Lock" and info = "alter table t1 auto_increment=0";
--source include/wait_condition.inc
unlock tables;
connection writer;
@@ -462,16 +462,16 @@ update t1 set i= 10;
connection reader;
let $wait_condition=
select count(*) = 1 from information_schema.processlist
- where state = "Locked" and info = "update t1 set i= 10";
+ where state = "Table Lock" and info = "update t1 set i= 10";
--source include/wait_condition.inc
send
select * from t1;
connection default;
let $wait_condition=
select count(*) = 1 from information_schema.processlist
- where state = "Locked" and info = "select * from t1";
+ where state = "Table Lock" and info = "select * from t1";
--source include/wait_condition.inc
-let $ID= `select id from information_schema.processlist where state = "Locked" and info = "update t1 set i= 10"`;
+let $ID= `select id from information_schema.processlist where state = "Table Lock" and info = "update t1 set i= 10"`;
--replace_result $ID ID
eval kill query $ID;
connection reader;
@@ -484,22 +484,6 @@ unlock tables;
connection default;
drop table t1;
-#
-# Bug#25856 HANDLER table OPEN in one connection lock DROP TABLE in another one
-#
---disable_warnings
-drop table if exists t1;
---enable_warnings
-create table t1 (a int) ENGINE=MEMORY;
---echo --> client 2
-connection locker;
---error ER_ILLEGAL_HA
-handler t1 open;
---echo --> client 1
-connection default;
-drop table t1;
-
-
# Disconnect sessions used in many subtests above
disconnect locker;
disconnect reader;
@@ -622,7 +606,7 @@ connection waiter;
connection default;
let $wait_condition=
select count(*) = 1 from information_schema.processlist
- where state = "Locked" and info = "insert into t1 values(1)";
+ where state = "Table Lock" and info = "insert into t1 values(1)";
--source include/wait_condition.inc
let $tlwb= `show status like 'Table_locks_waited'`;
unlock tables;
diff --git a/mysql-test/t/merge-big.test b/mysql-test/t/merge-big.test
index d39c2973688..10a41b609a0 100644
--- a/mysql-test/t/merge-big.test
+++ b/mysql-test/t/merge-big.test
@@ -52,7 +52,7 @@ connection default;
#--sleep 8
#SELECT ID,STATE,INFO FROM INFORMATION_SCHEMA.PROCESSLIST;
let $wait_condition= SELECT 1 FROM INFORMATION_SCHEMA.PROCESSLIST
- WHERE ID = $con1_id AND STATE = 'Locked';
+ WHERE ID = $con1_id AND STATE = "Table Lock";
--source include/wait_condition.inc
#SELECT NOW();
--echo # Kick INSERT out of thr_multi_lock().
diff --git a/mysql-test/t/multi_update.test b/mysql-test/t/multi_update.test
index d405e4e5e3a..e5c7a415170 100644
--- a/mysql-test/t/multi_update.test
+++ b/mysql-test/t/multi_update.test
@@ -498,9 +498,9 @@ send alter table t1 add column c int default 100 after a;
connect (updater,localhost,root,,test);
connection updater;
# Wait till "alter table t1 ..." of session changer is in work.
-# = There is one session is in state "Locked".
+# = There is one session is in state "Table Lock".
let $wait_condition= select count(*)= 1 from information_schema.processlist
- where state= 'Locked';
+ where state= "Table Lock";
--source include/wait_condition.inc
send update t1, v1 set t1.b=t1.a+t1.b+v1.b where t1.a=v1.a;
@@ -509,9 +509,9 @@ connection locker;
# - "alter table t1 ..." of session changer and
# - "update t1, v1 ..." of session updater
# are in work.
-# = There are two session is in state "Locked".
+# = There are two session is in state "Table Lock".
let $wait_condition= select count(*)= 2 from information_schema.processlist
- where state= 'Locked';
+ where state= "Table Lock";
--source include/wait_condition.inc
unlock tables;
diff --git a/mysql-test/t/multi_update2.test b/mysql-test/t/multi_update2.test
index 9c5078efb6f..a0f17fabec4 100644
--- a/mysql-test/t/multi_update2.test
+++ b/mysql-test/t/multi_update2.test
@@ -1,3 +1,5 @@
+--source include/long_test.inc
+
#
# Test of update statement that uses many tables.
#
diff --git a/mysql-test/t/pool_of_threads.test b/mysql-test/t/pool_of_threads.test
index e71b16e1f89..530038cee91 100644
--- a/mysql-test/t/pool_of_threads.test
+++ b/mysql-test/t/pool_of_threads.test
@@ -4,6 +4,7 @@
-- source include/have_pool_of_threads.inc
# Slow test, don't run during staging part
-- source include/not_staging.inc
+-- source include/long_test.inc
-- source include/common-tests.inc
diff --git a/mysql-test/t/query_cache.test b/mysql-test/t/query_cache.test
index de8467b509d..24487db826a 100644
--- a/mysql-test/t/query_cache.test
+++ b/mysql-test/t/query_cache.test
@@ -1,4 +1,5 @@
-- source include/have_query_cache.inc
+-- source include/long_test.inc
#
# Tests with query cache
diff --git a/mysql-test/t/query_cache_28249.test b/mysql-test/t/query_cache_28249.test
index 390a1ce6e3d..fd283aa69fb 100644
--- a/mysql-test/t/query_cache_28249.test
+++ b/mysql-test/t/query_cache_28249.test
@@ -64,12 +64,12 @@ connection user3;
# The values marked with 'X' must be reached.
--echo # Poll till the select of connection user1 is blocked by the write lock on t1.
let $wait_condition= SELECT COUNT(*) = 1 FROM information_schema.processlist
-WHERE state = 'Locked'
+WHERE state = "Table Lock"
AND info = '$select_for_qc';
--source include/wait_condition.inc
eval
SELECT user,command,state,info FROM information_schema.processlist
-WHERE state = 'Locked'
+WHERE state = "Table Lock"
AND info = '$select_for_qc';
INSERT INTO t1 VALUES (4);
diff --git a/mysql-test/t/sp_notembedded.test b/mysql-test/t/sp_notembedded.test
index e4b6a5deefe..9b34a23bcc9 100644
--- a/mysql-test/t/sp_notembedded.test
+++ b/mysql-test/t/sp_notembedded.test
@@ -276,7 +276,7 @@ set session low_priority_updates=on;
connection rl_wait;
let $wait_condition=
select count(*) = 1 from information_schema.processlist
- where state = "Locked" and
+ where state = "Table lock" and
info = "update t1 set value='updated' where value='old'";
--source include/wait_condition.inc
diff --git a/mysql-test/t/sp_sync.test b/mysql-test/t/sp_sync.test
index f9dae17b039..e7f952162ee 100644
--- a/mysql-test/t/sp_sync.test
+++ b/mysql-test/t/sp_sync.test
@@ -34,7 +34,7 @@ SET DEBUG_SYNC = 'multi_update_reopen_tables SIGNAL parked WAIT_FOR go';
connection con1;
let $wait_condition= SELECT 1 FROM information_schema.processlist WHERE ID = $ID AND
-state = "Locked";
+state = "Table lock";
--source include/wait_condition.inc
DROP TABLE t1, t2;
SET DEBUG_SYNC = 'now WAIT_FOR parked';
diff --git a/mysql-test/t/status.test b/mysql-test/t/status.test
index 5da210f5a69..505df0fe8dc 100644
--- a/mysql-test/t/status.test
+++ b/mysql-test/t/status.test
@@ -58,7 +58,7 @@ let $ID= `select connection_id()`;
connection con2;
--echo # Switched to connection: con2
# wait for the other query to start executing
-let $wait_condition= select 1 from INFORMATION_SCHEMA.PROCESSLIST where ID = $ID and STATE = "Locked";
+let $wait_condition= select 1 from INFORMATION_SCHEMA.PROCESSLIST where ID = $ID and STATE = "Table Lock";
--source include/wait_condition.inc
unlock tables;
diff --git a/mysql-test/t/subselect.test b/mysql-test/t/subselect.test
index 047987da3f7..9ff8dd63473 100644
--- a/mysql-test/t/subselect.test
+++ b/mysql-test/t/subselect.test
@@ -4230,6 +4230,21 @@ deallocate prepare stmt1;
drop table t1,t2;
+--echo #
+--echo # Bug LP#693935/#58727: Assertion failure with
+--echo # a single row subquery returning more than one row
+--echo #
+
+create table t1 (a char(1) charset utf8);
+insert into t1 values ('a'), ('b');
+create table t2 (a binary(1));
+insert into t2 values ('x'), ('y');
+
+-- error ER_SUBQUERY_NO_1_ROW
+select * from t2 where a=(select a from t1) and a='x';
+
+drop table t1,t2;
+
--echo End of 5.1 tests
--echo #
diff --git a/mysql-test/t/table_elim.test b/mysql-test/t/table_elim.test
index 7ad69d5bf37..5576362b396 100644
--- a/mysql-test/t/table_elim.test
+++ b/mysql-test/t/table_elim.test
@@ -467,3 +467,35 @@ HAVING
field4 != 6;
drop table t0,t1,t2,t3,t4,t5,t6;
+
+--echo #
+--echo # BUG#675118: Elimination of a table results in an invalid execution plan
+--echo #
+CREATE TABLE t1 (f1 int(11), PRIMARY KEY (f1)) ;
+
+CREATE TABLE t2 (f4 varchar(1024), KEY (f4)) ;
+INSERT IGNORE INTO t2 VALUES ('xcddwntkbxyorzdv'),
+ ('cnxxcddwntkbxyor'),('r'),('r'), ('did'),('I'),('when'),
+ ('hczkfqjeggivdvac'),('e'),('okay'),('up');
+
+CREATE TABLE t3 (f4 varchar(1024), f1 int(11), f2 int(11)) ;
+INSERT IGNORE INTO t3 VALUES ('f','4','0'),('n','5','-996540416');
+
+CREATE TABLE t4 (f1 int(11), f3 varchar(10)) ;
+INSERT IGNORE INTO t4 VALUES ('8','n'),('9','nwzcerzsgx'),('10','c');
+
+CREATE TABLE t5 (f5 int(11), KEY (f5)) ;
+
+EXPLAIN
+SELECT t3.f2
+FROM t2
+LEFT JOIN t3
+LEFT JOIN t4
+LEFT JOIN t1 ON t4.f1 = t1.f1
+JOIN t5 ON t4.f3 ON t3.f1 = t5.f5 ON t2.f4 = t3.f4
+WHERE t3.f2 ;
+--echo # ^^ The above must not produce a QEP of t3,t5,t2,t4
+--echo # as that violates the "no interleaving of outer join nests" rule.
+
+DROP TABLE t1,t2,t3,t4,t5;
+
diff --git a/mysql-test/t/trigger_notembedded.test b/mysql-test/t/trigger_notembedded.test
index f595f195dcc..16e9b2361f9 100644
--- a/mysql-test/t/trigger_notembedded.test
+++ b/mysql-test/t/trigger_notembedded.test
@@ -916,7 +916,7 @@ INSERT INTO t1 VALUES (5);
CONNECTION rl_contender;
# Wait until wl_acquirer is waiting for the read lock on t2 to be released.
let $wait_condition=
- SELECT STATE = 'Locked' FROM INFORMATION_SCHEMA.PROCESSLIST
+ SELECT STATE = "Table Lock" FROM INFORMATION_SCHEMA.PROCESSLIST
WHERE ID = $wl_acquirer_thread_id;
--source include/wait_condition.inc
# must not "see" the row inserted by the INSERT (as it must run before the
diff --git a/mysys/thr_alarm.c b/mysys/thr_alarm.c
index 15c24facac0..98c746af8ca 100644
--- a/mysys/thr_alarm.c
+++ b/mysys/thr_alarm.c
@@ -36,7 +36,7 @@
uint thr_client_alarm;
static int alarm_aborted=1; /* No alarm thread */
-my_bool thr_alarm_inited= 0;
+my_bool thr_alarm_inited= 0, my_disable_thr_alarm= 0;
volatile my_bool alarm_thread_running= 0;
time_t next_alarm_expire_time= ~ (time_t) 0;
static sig_handler process_alarm_part2(int sig);
@@ -173,6 +173,21 @@ my_bool thr_alarm(thr_alarm_t *alrm, uint sec, ALARM *alarm_data)
DBUG_ENTER("thr_alarm");
DBUG_PRINT("enter",("thread: %s sec: %d",my_thread_name(),sec));
+ if (my_disable_thr_alarm)
+ {
+ (*alrm)= &alarm_data->alarmed;
+ alarm_data->alarmed= 1; /* Abort if interrupted */
+ DBUG_RETURN(0);
+ }
+
+ if (unlikely(alarm_aborted))
+ { /* No signal thread */
+ DBUG_PRINT("info", ("alarm aborted"));
+ if (alarm_aborted > 0)
+ goto abort_no_unlock;
+ sec= 1; /* Abort mode */
+ }
+
now= my_time(0);
if (!alarm_data)
{
@@ -190,13 +205,6 @@ my_bool thr_alarm(thr_alarm_t *alrm, uint sec, ALARM *alarm_data)
one_signal_hand_sigmask(SIG_BLOCK,&full_signal_set,&old_mask);
pthread_mutex_lock(&LOCK_alarm); /* Lock from threads & alarms */
- if (unlikely(alarm_aborted))
- { /* No signal thread */
- DBUG_PRINT("info", ("alarm aborted"));
- if (alarm_aborted > 0)
- goto abort;
- sec= 1; /* Abort mode */
- }
if (alarm_queue.elements >= max_used_alarms)
{
if (alarm_queue.elements == alarm_queue.max_elements)
@@ -251,6 +259,8 @@ void thr_end_alarm(thr_alarm_t *alarmed)
#endif
DBUG_ENTER("thr_end_alarm");
+ if (my_disable_thr_alarm)
+ DBUG_VOID_RETURN;
one_signal_hand_sigmask(SIG_BLOCK,&full_signal_set,&old_mask);
alarm_data= (ALARM*) ((uchar*) *alarmed - offsetof(ALARM,alarmed));
pthread_mutex_lock(&LOCK_alarm);
diff --git a/mysys/thr_lock.c b/mysys/thr_lock.c
index 68a3caf6da7..32b258c0536 100644
--- a/mysys/thr_lock.c
+++ b/mysys/thr_lock.c
@@ -228,14 +228,16 @@ static void check_locks(THR_LOCK *lock, const char *where,
if (found_errors < MAX_FOUND_ERRORS)
{
- uint count=0;
+ uint count=0, count2= 0;
THR_LOCK_DATA *data;
for (data=lock->read.data ; data ; data=data->next)
{
+ count2++;
if (data->type == TL_READ_NO_INSERT)
count++;
/* Protect against infinite loop. */
- DBUG_ASSERT(count <= lock->read_no_write_count);
+ DBUG_ASSERT(count <= lock->read_no_write_count &&
+ count2 <= MAX_LOCKS);
}
if (count != lock->read_no_write_count)
{
@@ -286,7 +288,10 @@ static void check_locks(THR_LOCK *lock, const char *where,
if (lock->write.data->type == TL_WRITE_CONCURRENT_INSERT)
{
THR_LOCK_DATA *data;
- for (data=lock->write.data->next ; data ; data=data->next)
+ uint count= 0;
+ for (data=lock->write.data->next;
+ data && count < MAX_LOCKS;
+ data=data->next)
{
if (data->type != TL_WRITE_CONCURRENT_INSERT)
{
diff --git a/mysys/tree.c b/mysys/tree.c
index c8571fd0337..eba058b22f8 100644
--- a/mysys/tree.c
+++ b/mysys/tree.c
@@ -378,6 +378,7 @@ void *tree_search_key(TREE *tree, const void *key,
case HA_READ_KEY_EXACT:
case HA_READ_KEY_OR_NEXT:
case HA_READ_BEFORE_KEY:
+ case HA_READ_KEY_OR_PREV:
last_equal_element= parents;
cmp= 1;
break;
@@ -421,6 +422,9 @@ void *tree_search_key(TREE *tree, const void *key,
case HA_READ_BEFORE_KEY:
*last_pos= last_right_step_parent;
break;
+ case HA_READ_KEY_OR_PREV:
+ *last_pos= last_equal_element ? last_equal_element : last_right_step_parent;
+ break;
default:
return NULL;
}
diff --git a/plugin/handler_socket/AUTHORS b/plugin/handler_socket/AUTHORS
new file mode 100644
index 00000000000..6a66e0ac799
--- /dev/null
+++ b/plugin/handler_socket/AUTHORS
@@ -0,0 +1,19 @@
+Akira Higuchi (https://github.com/ahiguti)
+ - developed HanderSocket plugin, libhsclient, and perl-Net-HandlerSocket
+
+Yoshinori Matsunobu (https://github.com/yoshinorim)
+ - introduced autotools, added support for MySQL 5.5.6, added statistics
+ variables
+
+Jeff Hodges (https://github.com/jmhodges)
+ - fixed some autotools scripts
+
+Toru Yamaguchi (https://github.com/zigorou)
+ - ported to MacOS X
+
+Moriyoshi Koizumi (https://github.com/moriyoshi)
+ - fixed some autotools scripts
+
+takeda-at (https://github.com/takada-at)
+ - added simple authorization function
+
diff --git a/plugin/handler_socket/ChangeLog b/plugin/handler_socket/ChangeLog
new file mode 100644
index 00000000000..793a39937e5
--- /dev/null
+++ b/plugin/handler_socket/ChangeLog
@@ -0,0 +1,19 @@
+1.0.6 - For MariaDB
+ * Modifications to Makefiles to be part of plugin directory
+ * Compiled by default in max builds
+ * Some minor changes in database.cpp to use the new MariaDB handler
+ interface
+o * Fixed compiler warnings
+
+1.0.6 - 2010-10-29
+ * Changed build instruction (autoreconf/configure/make), removed auto-generated files (Contributed by jmhodges)
+ *
+
+1.0.5 - 2010-10-18
+ * Changed build procedures (using typical configure/make)
+ * Supported 5.5.6
+ * Added status variables
+
+1.0.4 - 2010-08-15
+ * Initial public release
+
diff --git a/plugin/handler_socket/Makefile.am b/plugin/handler_socket/Makefile.am
new file mode 100644
index 00000000000..7dff19820ce
--- /dev/null
+++ b/plugin/handler_socket/Makefile.am
@@ -0,0 +1,88 @@
+
+ACLOCAL_AMFLAGS = -I m4
+
+SUBDIRS = @HANDLERSOCKET_SUBDIRS@
+EXTRA_DIST= plug.in
+
+perl:
+ cd perl-Net-HandlerSocket && perl Makefile.PL && make
+
+install_perl:
+ cd perl-Net-HandlerSocket && make install
+
+rpms: rpm_cli rpm_perl rpm_c
+
+rpm_dir:
+ - mkdir dist
+ - mkdir dist/BUILD dist/RPMS dist/SOURCES dist/SPECS dist/SRPMS
+
+rpm_cli: clean_cli rpm_dir
+ sed -e "s/HANDLERSOCKET_VERSION/$(VERSION)/" \
+ libhsclient/libhsclient.spec.template \
+ > libhsclient/libhsclient.spec
+ tar cvfz dist/libhsclient.tar.gz libhsclient
+ rpmbuild --define "_topdir `pwd`/dist" -ta \
+ dist/libhsclient.tar.gz
+
+rpm_perl: clean_perl rpm_dir
+ sed -e "s/HANDLERSOCKET_VERSION/$(VERSION)/" \
+ perl-Net-HandlerSocket/perl-Net-HandlerSocket.spec.template \
+ > perl-Net-HandlerSocket/perl-Net-HandlerSocket.spec
+ cd perl-Net-HandlerSocket && perl Makefile.PL && make clean && \
+ rm -f Makefile.old
+ tar cvfz dist/perl-Net-HandlerSocket.tar.gz perl-Net-HandlerSocket
+ rpmbuild --define "_topdir `pwd`/dist" -ta \
+ dist/perl-Net-HandlerSocket.tar.gz
+
+rpm_c: clean_c rpm_dir
+ sed -e "s/HANDLERSOCKET_VERSION/$(VERSION)/" \
+ handlersocket/handlersocket.spec.template \
+ > handlersocket/handlersocket.spec
+ sed -e "s|HANDLERSOCKET_MYSQL_INC|$(MYSQL_CFLAGS) $(MYSQL_INC)|" \
+ -e "s|HANDLERSOCKET_MYSQL_LIB|$(MYSQL_LIB)|" \
+ handlersocket/Makefile.plain.template \
+ > handlersocket/Makefile.plain
+ tar cvfz dist/handlersocket.tar.gz handlersocket
+ rpmbuild --define "_topdir `pwd`/dist" -ta \
+ dist/handlersocket.tar.gz
+
+install_rpm_pl:
+ - sudo rpm -e perl-Net-HandlerSocket
+ - sudo rpm -e perl-Net-HandlerSocket-debuginfo
+ make clean
+ make rpm_perl
+ - sudo rpm -U dist/RPMS/*/perl*.rpm
+
+installrpms:
+ - sudo rpm -e handlersocket
+ - sudo rpm -e handlersocket-debuginfo
+ - sudo rpm -e perl-Net-HandlerSocket
+ - sudo rpm -e perl-Net-HandlerSocket-debuginfo
+ - sudo rpm -e libhsclient
+ - sudo rpm -e libhsclient-debuginfo
+ make clean
+ make rpm_cli
+ - sudo rpm -U dist/RPMS/*/libhsclient*.rpm
+ make clean
+ make rpm_perl
+ - sudo rpm -U dist/RPMS/*/perl*.rpm
+ make clean
+ make rpm_c
+ - sudo rpm -U dist/RPMS/*/handlersocket*.rpm
+
+clean_cli:
+ cd libhsclient && make clean
+ cd client && make clean
+
+clean_perl:
+ cd perl-Net-HandlerSocket && perl Makefile.PL && make clean && \
+ rm -f Makefile.old
+
+clean_c:
+ cd handlersocket && make clean
+
+clean_all: clean_cli clean_perl clean_c
+ cd regtest && make clean
+ rm -rf dist/*/*
+ rm -f dist/*.tar.gz
+
diff --git a/plugin/handler_socket/README b/plugin/handler_socket/README
new file mode 100644
index 00000000000..9a3bed7ae65
--- /dev/null
+++ b/plugin/handler_socket/README
@@ -0,0 +1,82 @@
+Notes added by Monty:
+
+This is HandlerSocket version 1.0.6 (See ChangeLog file)
+The original code can be found at:
+
+https://github.com/ahiguti/HandlerSocket-Plugin-for-MySQL
+
+-----------------------------------------------------------------------------
+HandlerSocket plugin for MySQL
+
+Copyright (c) 2010 DeNA Co.,Ltd.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of DeNA Co.,Ltd. nor the names of its contributors
+ may be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY DeNA Co.,Ltd. "AS IS" AND ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+EVENT SHALL DeNA Co.,Ltd. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+-----------------------------------------------------------------------------
+About HandlerSocket
+
+HandlerSocket is a NoSQL plugin for MySQL. It works as a daemon inside the
+mysqld process, accept tcp connections, and execute requests from clients.
+HandlerSocket does not support SQL queries. Instead, it supports simple CRUD
+operations on tables.
+
+Because of the following reasons, HandlerSocket is much faster than the
+mysqld/libmysql pair in some circumstances:
+
+ - HandlerSocket manipulates data without parsing SQL, which causes less
+ CPU usage.
+ - HandlerSocket reads many requests from clients and executes their
+ requests in bulk, which causes less CPU and disk usage.
+ - HandlerSocket client/server protocol is more compact than the
+ mysql/libmysql pair, which causes less network usage.
+
+The current version of HandlerSocket only works with GNU/Linux. The source
+archive of HandlerSocket includes a C++ and a Perl client libraries.
+Here is a list of client libraries for other languages:
+
+ - PHP
+ http://openpear.org/package/Net_HandlerSocket
+ http://github.com/tz-lom/HSPHP
+ http://code.google.com/p/php-handlersocket/
+ - Java
+ http://code.google.com/p/hs4j/
+ http://code.google.com/p/handlersocketforjava/
+ - Python
+ http://pypi.python.org/pypi/python-handler-socket
+ https://code.launchpad.net/~songofacandy/+junk/pyhandlersocket
+ - Ruby
+ https://github.com/winebarrel/ruby-handlersocket
+ https://github.com/miyucy/handlersocket
+ - JavaScript
+ https://github.com/koichik/node-handlersocket
+ - Scala
+ https://github.com/fujohnwang/hs2client
+
+The home of HandlerSocket is here:
+ https://github.com/ahiguti/HandlerSocket-Plugin-for-MySQL
+
+More documents are available in docs-en/ and docs-ja/ directories.
+
diff --git a/plugin/handler_socket/autogen.sh b/plugin/handler_socket/autogen.sh
new file mode 100755
index 00000000000..3b80afd1cb8
--- /dev/null
+++ b/plugin/handler_socket/autogen.sh
@@ -0,0 +1,117 @@
+#!/bin/sh
+
+warn() {
+echo -e "\tWARNING: $@" 1>&2
+}
+
+# init
+
+LIBTOOLIZE=libtoolize
+ACLOCAL=aclocal
+AUTOCONF=autoconf
+AUTOHEADER=autoheader
+AUTOMAKE=automake
+
+case `uname -s` in
+Darwin)
+LIBTOOLIZE=glibtoolize
+;;
+FreeBSD)
+ACLOCAL_ARGS="$ACLOCAL_ARGS -I /usr/local/share/aclocal/"
+;;
+esac
+
+
+# libtoolize
+echo "Searching libtoolize..."
+if [ `which $LIBTOOLIZE` ] ; then
+echo -e "\tFOUND: libtoolize -> $LIBTOOLIZE"
+else
+warn "Cannot Found libtoolize... input libtool command"
+ read LIBTOOLIZE
+ LIBTOOLIZE=`which $LIBTOOLIZE`
+ if [ `which $LIBTOOLIZE` ] ; then
+echo -e "\tSET: libtoolize -> $LIBTOOLIZE"
+ else
+warn "$LIBTOOLIZE: Command not found."
+ exit 1;
+ fi
+fi
+
+# aclocal
+echo "Searching aclocal..."
+if [ `which $ACLOCAL` ] ; then
+echo -e "\tFOUND: aclocal -> $ACLOCAL"
+else
+warn "Cannot Found aclocal... input aclocal command"
+ read ACLOCAL
+ ACLOCAL=`which $ACLOCAL`
+ if [ `which $ACLOCAL` ] ; then
+echo -e "\tSET: aclocal -> $ACLOCAL"
+ else
+warn "$ACLOCAL: Command not found."
+ exit 1;
+ fi
+fi
+
+# automake
+echo "Searching automake..."
+if [ `which $AUTOMAKE` ] ; then
+echo -e "\tFOUND: automake -> $AUTOMAKE"
+else
+warn "Cannot Found automake... input automake command"
+ read AUTOMAKE
+ ACLOCAL=`which $AUTOMAKE`
+ if [ `which $AUTOMAKE` ] ; then
+echo -e "\tSET: automake -> $AUTOMAKE"
+ else
+warn "$AUTOMAKE: Command not found."
+ exit 1;
+ fi
+fi
+
+# autoheader
+echo "Searching autoheader..."
+if [ `which $AUTOHEADER` ] ; then
+echo -e "\tFOUND: autoheader -> $AUTOHEADER"
+else
+warn "Cannot Found autoheader... input autoheader command"
+ read AUTOHEADER
+ ACLOCAL=`which $AUTOHEADER`
+ if [ `which $AUTOHEADER` ] ; then
+echo -e "\tSET: autoheader -> $AUTOHEADER"
+ else
+warn "$AUTOHEADER: Command not found."
+ exit 1;
+ fi
+fi
+
+# autoconf
+echo "Searching autoconf..."
+if [ `which $AUTOCONF` ] ; then
+echo -e "\tFOUND: autoconf -> $AUTOCONF"
+else
+warn "Cannot Found autoconf... input autoconf command"
+ read AUTOCONF
+ ACLOCAL=`which $AUTOCONF`
+ if [ `which $AUTOCONF` ] ; then
+echo -e "\tSET: autoconf -> $AUTOCONF"
+ else
+warn "$AUTOCONF: Command not found."
+ exit 1;
+ fi
+fi
+
+echo "Running libtoolize ..."
+$LIBTOOLIZE --force --copy
+echo "Running aclocal ..."
+$ACLOCAL ${ACLOCAL_ARGS} -I .
+echo "Running autoheader..."
+$AUTOHEADER
+echo "Running automake ..."
+$AUTOMAKE --add-missing --copy
+echo "Running autoconf ..."
+$AUTOCONF
+
+mkdir m4 2> /dev/null
+
diff --git a/plugin/handler_socket/client/Makefile.am b/plugin/handler_socket/client/Makefile.am
new file mode 100644
index 00000000000..6aff3419565
--- /dev/null
+++ b/plugin/handler_socket/client/Makefile.am
@@ -0,0 +1,14 @@
+CXXFLAGS += -fimplicit-templates
+AM_INCLUDES= -I$(srcdir)/../libhsclient
+bin_PROGRAMS=hsclient
+hsclient_SOURCES= hsclient.cpp
+hsclient_LDFLAGS= -static -L../libhsclient -lhsclient
+hsclient_CXXFLAGS= $(AM_INCLUDES)
+
+hstest: hstest.o
+ $(CXX) $(CXXFLAGS) $(LFLAGS) hstest.o \
+ -L../libhsclient/.libs -lhsclient $(MYSQL_LIB) -o hstest
+
+hstest.o: hstest.cpp
+ $(CXX) $(CXXFLAGS) $(MYSQL_INC) $(AM_INCLUDES) -c hstest.cpp
+
diff --git a/plugin/handler_socket/client/hsclient.cpp b/plugin/handler_socket/client/hsclient.cpp
new file mode 100644
index 00000000000..0dd8332e345
--- /dev/null
+++ b/plugin/handler_socket/client/hsclient.cpp
@@ -0,0 +1,88 @@
+
+// vim:sw=2:ai
+
+#include "hstcpcli.hpp"
+#include "string_util.hpp"
+
+namespace dena {
+
+int
+hstcpcli_main(int argc, char **argv)
+{
+ config conf;
+ parse_args(argc, argv, conf);
+ socket_args sockargs;
+ sockargs.set(conf);
+ hstcpcli_ptr cli = hstcpcli_i::create(sockargs);
+ const std::string dbname = conf.get_str("dbname", "hstest");
+ const std::string table = conf.get_str("table", "hstest_table1");
+ const std::string index = conf.get_str("index", "PRIMARY");
+ const std::string fields = conf.get_str("fields", "k,v");
+ const int limit = conf.get_int("limit", 0);
+ const int skip = conf.get_int("skip", 0);
+ std::vector<std::string> keys;
+ std::vector<string_ref> keyrefs;
+ size_t num_keys = 0;
+ while (true) {
+ const std::string conf_key = std::string("k") + to_stdstring(num_keys);
+ const std::string k = conf.get_str(conf_key, "");
+ const std::string kx = conf.get_str(conf_key, "x");
+ if (k.empty() && kx == "x") {
+ break;
+ }
+ ++num_keys;
+ keys.push_back(k);
+ }
+ for (size_t i = 0; i < keys.size(); ++i) {
+ const string_ref ref(keys[i].data(), keys[i].size());
+ keyrefs.push_back(ref);
+ }
+ const std::string op = conf.get_str("op", "=");
+ const string_ref op_ref(op.data(), op.size());
+ cli->request_buf_open_index(0, dbname.c_str(), table.c_str(),
+ index.c_str(), fields.c_str());
+ cli->request_buf_exec_generic(0, op_ref, num_keys == 0 ? 0 : &keyrefs[0],
+ num_keys, limit, skip, string_ref(), 0, 0);
+ int code = 0;
+ size_t numflds = 0;
+ do {
+ if (cli->request_send() != 0) {
+ fprintf(stderr, "request_send: %s\n", cli->get_error().c_str());
+ break;
+ }
+ if ((code = cli->response_recv(numflds)) != 0) {
+ fprintf(stderr, "response_recv: %s\n", cli->get_error().c_str());
+ break;
+ }
+ } while (false);
+ cli->response_buf_remove();
+ do {
+ if ((code = cli->response_recv(numflds)) != 0) {
+ fprintf(stderr, "response_recv: %s\n", cli->get_error().c_str());
+ break;
+ }
+ while (true) {
+ const string_ref *const row = cli->get_next_row();
+ if (row == 0) {
+ break;
+ }
+ printf("REC:");
+ for (size_t i = 0; i < numflds; ++i) {
+ const std::string val(row[i].begin(), row[i].size());
+ printf(" %s", val.c_str());
+ }
+ printf("\n");
+ }
+ } while (false);
+ cli->response_buf_remove();
+ return 0;
+}
+
+};
+
+int
+main(int argc, char **argv)
+{
+ return dena::hstcpcli_main(argc, argv);
+}
+
diff --git a/plugin/handler_socket/client/hspool_test.pl b/plugin/handler_socket/client/hspool_test.pl
new file mode 100755
index 00000000000..7fe073301b1
--- /dev/null
+++ b/plugin/handler_socket/client/hspool_test.pl
@@ -0,0 +1,224 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+use DB::HandlerSocket::Pool;
+use DBI;
+
+my %conf = ();
+for my $i (@ARGV) {
+ my ($k, $v) = split(/=/, $i);
+ $conf{$k} = $v;
+}
+
+my $verbose = get_conf("verbose", 0);
+my $actions_str = get_conf("actions",
+ "create,insert,verify,verify2,verify3,verify4,clean");
+my $tablesize = get_conf("tablesize", 1000);
+my $db = get_conf("db", "hstestdb");
+my $table = get_conf("table", "testtbl");
+my $table_schema = get_conf("table_schema", undef);
+my $engine = get_conf("engine", "innodb");
+my $host = get_conf("host", "localhost");
+my $mysqlport = get_conf("mysqlport", 3306);
+my $hsport_rd = get_conf("hsport_rd", 9998);
+my $hsport_wr = get_conf("hsport_wr", 9999);
+my $loop = get_conf("loop", 10000);
+my $op = get_conf("op", "=");
+my $ssps = get_conf("ssps", 0);
+my $num_moreflds = get_conf("moreflds", 0);
+my $moreflds_prefix = get_conf("moreflds_prefix", "f");
+my $mysql_user = 'root';
+my $mysql_password = '';
+
+my $dsn = "DBI:mysql:database=;host=$host;port=$mysqlport"
+ . ";mysql_server_prepare=$ssps";
+my $dbh = DBI->connect($dsn, $mysql_user, $mysql_password,
+ { RaiseError => 1 });
+my $hsargs = { 'host' => $host, 'port' => $hsport_rd };
+my $hspool = new DB::HandlerSocket::Pool({
+ hostmap => {
+ "$db.$table" => {
+ host => $host,
+ port => $hsport_rd,
+ },
+ },
+ resolve => undef,
+ error => undef,
+});
+$table_schema = "(k int primary key, fc30 varchar(30), ft text)"
+ if (!defined($table_schema));
+
+my @actions = split(/,/, $actions_str);
+for my $action (@actions) {
+ print "ACTION: $action\n";
+ eval "hstest_$action()";
+ if ($@) {
+ die $@;
+ }
+ print "ACTION: $action DONE\n";
+}
+
+sub get_conf {
+ my ($key, $def) = @_;
+ my $val = $conf{$key};
+ if ($val) {
+ print "$key=$val\n";
+ } else {
+ $val = $def;
+ my $defstr = $def || "(undef)";
+ print "$key=$defstr(default)\n";
+ }
+ return $val;
+}
+
+sub hstest_create {
+ $dbh->do("drop database if exists $db");
+ $dbh->do("create database $db");
+ $dbh->do("use $db");
+ $dbh->do("create table $table $table_schema engine=$engine");
+}
+
+sub hstest_dump {
+ $dbh->do("use $db");
+ my $sth = $dbh->prepare("select * from $table");
+ $sth->execute();
+ my $arr = $sth->fetchall_arrayref();
+ for my $rec (@$arr) {
+ print "REC:";
+ for my $row (@$rec) {
+ print " $row";
+ }
+ print "\n";
+ }
+}
+
+sub hstest_insert {
+ $dbh->do("use $db");
+ my $sth = $dbh->prepare("insert into $table values (?, ?, ?)");
+ for (my $k = 0; $k < $tablesize; ++$k) {
+ my $fc30 = "fc30_$k";
+ my $ft = "ft_$k";
+ $sth->execute($k, $fc30, $ft);
+ }
+}
+
+sub hstest_verify {
+ $dbh->do("use $db");
+ my $sth = $dbh->prepare("select * from $table order by k");
+ $sth->execute();
+ my $arr = $sth->fetchall_arrayref();
+ my $hsres = $hspool->index_find($db, $table, "PRIMARY", "k,fc30,ft",
+ ">=", [ 0 ], $tablesize, 0);
+ for (my $i = 0; $i < $tablesize; ++$i) {
+ my $rec = $arr->[$i];
+ my $differ = 0;
+ print "REC:" if $verbose;
+ for (my $j = 0; $j < 3; ++$j) {
+ my $fld = $rec->[$j];
+ my $hsidx = $i * 3 + $j;
+ my $hsfld = $hsres->[$hsidx];
+ if ($hsfld ne $fld) {
+ $differ = 1;
+ }
+ if ($differ) {
+ print " $fld:$hsfld" if $verbose;
+ } else {
+ print " $hsfld" if $verbose;
+ }
+ }
+ print "\n" if $verbose;
+ if ($differ) {
+ die "verification failed";
+ }
+ }
+}
+
+sub hstest_verify2 {
+ $dbh->do("use $db");
+ my $sth = $dbh->prepare("select * from $table order by k");
+ $sth->execute();
+ my $arr = $sth->fetchall_arrayref();
+ my $hsresa = $hspool->index_find_multi($db, $table, "PRIMARY",
+ "k,fc30,ft", [ [ -1, ">=", [ 0 ], $tablesize, 0 ] ]);
+ my $hsres = $hsresa->[0];
+ for (my $i = 0; $i < $tablesize; ++$i) {
+ my $rec = $arr->[$i];
+ my $differ = 0;
+ print "REC:" if $verbose;
+ for (my $j = 0; $j < 3; ++$j) {
+ my $fld = $rec->[$j];
+ my $hsidx = $i * 3 + $j;
+ my $hsfld = $hsres->[$hsidx];
+ if ($hsfld ne $fld) {
+ $differ = 1;
+ }
+ if ($differ) {
+ print " $fld:$hsfld" if $verbose;
+ } else {
+ print " $hsfld" if $verbose;
+ }
+ }
+ print "\n" if $verbose;
+ if ($differ) {
+ die "verification failed";
+ }
+ }
+}
+
+sub hashref_to_str {
+ my $href = $_[0];
+ my $r = '';
+ for my $k (sort keys %$href) {
+ my $v = $href->{$k};
+ $r .= "($k=>$v)";
+ }
+ return $r;
+}
+
+sub hstest_verify3 {
+ $dbh->do("use $db");
+ my $sth = $dbh->prepare("select * from $table order by k");
+ $sth->execute();
+ my $hsres_t = $hspool->index_find($db, $table, "PRIMARY", "k,fc30,ft",
+ ">=", [ 0 ], $tablesize, 0);
+ my $hsres = DB::HandlerSocket::Pool::result_single_to_hasharr(
+ [ 'k', 'fc30', 'ft' ], $hsres_t);
+ for (my $i = 0; $i < $tablesize; ++$i) {
+ my $mystr = hashref_to_str($sth->fetchrow_hashref());
+ my $hsstr = hashref_to_str($hsres->[$i]);
+ if ($mystr ne $hsstr) {
+ print "DIFF my=[$mystr] hs=[$hsstr]\n" if $verbose;
+ die "verification failed";
+ } else {
+ print "OK $hsstr\n" if $verbose;
+ }
+ }
+}
+
+sub hstest_verify4 {
+ $dbh->do("use $db");
+ my $sth = $dbh->prepare("select * from $table order by k");
+ $sth->execute();
+ my $hsres_t = $hspool->index_find($db, $table, "PRIMARY", "k,fc30,ft",
+ ">=", [ 0 ], $tablesize, 0);
+ my $hsres = DB::HandlerSocket::Pool::result_single_to_hashhash(
+ [ 'k', 'fc30', 'ft' ], 'k', $hsres_t);
+ my $rechash = $sth->fetchall_hashref('k');
+ while (my ($k, $href) = each (%$rechash)) {
+ my $mystr = hashref_to_str($href);
+ my $hsstr = hashref_to_str($hsres->{$k});
+ if ($mystr ne $hsstr) {
+ print "DIFF my=[$mystr] hs=[$hsstr]\n" if $verbose;
+ die "verification failed";
+ } else {
+ print "OK $hsstr\n" if $verbose;
+ }
+ }
+}
+
+sub hstest_clean {
+ $hspool->clear_pool();
+ $dbh->do("drop database if exists $db");
+}
+
diff --git a/plugin/handler_socket/client/hstest.cpp b/plugin/handler_socket/client/hstest.cpp
new file mode 100644
index 00000000000..c766488cbdf
--- /dev/null
+++ b/plugin/handler_socket/client/hstest.cpp
@@ -0,0 +1,1494 @@
+
+// vim:sw=2:ai
+
+#include <signal.h>
+#include <sys/time.h>
+#include <stdio.h>
+#include <string.h>
+#include <vector>
+#include <stdlib.h>
+#include <memory>
+#include <errno.h>
+#include <mysql.h>
+#include <time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include "util.hpp"
+#include "auto_ptrcontainer.hpp"
+#include "socket.hpp"
+#include "thread.hpp"
+#include "hstcpcli.hpp"
+
+#if __GNUC__ >= 4
+long atomic_exchange_and_add(volatile long *valp, long c)
+{
+ return __sync_fetch_and_add(valp, c);
+}
+#else
+#include <bits/atomicity.h>
+using namespace __gnu_cxx;
+long atomic_exchange_and_add(volatile long *valp, long c)
+{
+ return __exchange_and_add((volatile _Atomic_word *)valp, c);
+}
+#endif
+
+namespace dena {
+
+struct auto_mysql : private noncopyable {
+ auto_mysql() : db(0) {
+ reset();
+ }
+ ~auto_mysql() {
+ if (db) {
+ mysql_close(db);
+ }
+ }
+ void reset() {
+ if (db) {
+ mysql_close(db);
+ }
+ if ((db = mysql_init(0)) == 0) {
+ fatal_abort("failed to initialize mysql client");
+ }
+ }
+ operator MYSQL *() const { return db; }
+ private:
+ MYSQL *db;
+};
+
+struct auto_mysql_res : private noncopyable {
+ auto_mysql_res(MYSQL *db) {
+ res = mysql_store_result(db);
+ }
+ ~auto_mysql_res() {
+ if (res) {
+ mysql_free_result(res);
+ }
+ }
+ operator MYSQL_RES *() const { return res; }
+ private:
+ MYSQL_RES *res;
+};
+
+struct auto_mysql_stmt : private noncopyable {
+ auto_mysql_stmt(MYSQL *db) {
+ stmt = mysql_stmt_init(db);
+ }
+ ~auto_mysql_stmt() {
+ if (stmt) {
+ mysql_stmt_close(stmt);
+ }
+ }
+ operator MYSQL_STMT *() const { return stmt; }
+ private:
+ MYSQL_STMT *stmt;
+};
+
+namespace {
+
+double
+gettimeofday_double()
+{
+ struct timeval tv = { };
+ if (gettimeofday(&tv, 0) != 0) {
+ fatal_abort("gettimeofday");
+ }
+ return static_cast<double>(tv.tv_usec) / 1000000 + tv.tv_sec;
+}
+
+// unused
+void
+wait_close(int fd)
+{
+ char buf[1024];
+ while (true) {
+ int r = read(fd, buf, sizeof(buf));
+ if (r <= 0) {
+ break;
+ }
+ }
+}
+
+// unused
+void
+gentle_close(int fd)
+{
+ int r = shutdown(fd, SHUT_WR);
+ if (r != 0) {
+ return;
+ }
+ wait_close(fd);
+}
+
+};
+
+struct hstest_shared {
+ config conf;
+ socket_args arg;
+ int verbose;
+ size_t loop;
+ size_t pipe;
+ char op;
+ long num_threads;
+ mutable volatile long count;
+ mutable volatile long conn_count;
+ long wait_conn;
+ volatile char *keygen;
+ long keygen_size;
+ mutable volatile int enable_timing;
+ int usleep;
+ int dump;
+ hstest_shared() : verbose(0), loop(0), pipe(0), op('G'), num_threads(0),
+ count(0), conn_count(0), wait_conn(0), keygen(0), keygen_size(0),
+ enable_timing(0), usleep(0), dump(0) { }
+ void increment_count(unsigned int c = 1) const volatile {
+ atomic_exchange_and_add(&count, c);
+ }
+ void increment_conn(unsigned int c) const volatile {
+ atomic_exchange_and_add(&conn_count, c);
+ while (wait_conn != 0 && conn_count < wait_conn) {
+ sleep(1);
+ }
+ // fprintf(stderr, "wait_conn=%ld done\n", wait_conn);
+ }
+};
+
+struct hstest_thread {
+ struct arg_type {
+ size_t id;
+ const hstest_shared& sh;
+ bool watch_flag;
+ arg_type(size_t i, const hstest_shared& s, bool w)
+ : id(i), sh(s), watch_flag(w) { }
+ };
+ hstest_thread(const arg_type& a) : arg(a), io_success_count(0),
+ op_success_count(0), response_min(99999), response_max(0),
+ response_sum(0), response_avg(0) { }
+ void operator ()();
+ void test_1();
+ void test_2_3(int test_num);
+ void test_4_5(int test_num);
+ void test_6(int test_num);
+ void test_7(int test_num);
+ void test_8(int test_num);
+ void test_9(int test_num);
+ void test_10(int test_num);
+ void test_11(int test_num);
+ void test_12(int test_num);
+ void test_21(int test_num);
+ void test_22(int test_num);
+ void test_watch();
+ void sleep_if();
+ void set_timing(double time_spent);
+ arg_type arg;
+ auto_file fd;
+ size_t io_success_count;
+ size_t op_success_count;
+ double response_min, response_max, response_sum, response_avg;
+};
+
+void
+hstest_thread::test_1()
+{
+ char buf[1024];
+ unsigned int seed = arg.id;
+ seed ^= arg.sh.conf.get_int("seed_xor", 0);
+ std::string err;
+ if (socket_connect(fd, arg.sh.arg, err) != 0) {
+ fprintf(stderr, "connect: %d %s\n", errno, strerror(errno));
+ return;
+ }
+ const char op = arg.sh.op;
+ const int tablesize = arg.sh.conf.get_int("tablesize", 0);
+ for (size_t i = 0; i < arg.sh.loop; ++i) {
+ for (size_t j = 0; j < arg.sh.pipe; ++j) {
+ int k = 0, v = 0, len = 0;
+ if (op == 'G') {
+ k = rand_r(&seed);
+ v = rand_r(&seed); /* unused */
+ if (tablesize != 0) {
+ k &= tablesize;
+ }
+ len = snprintf(buf, sizeof(buf), "%c\tk%d\n", op, k);
+ } else {
+ k = rand_r(&seed);
+ v = rand_r(&seed);
+ if (tablesize != 0) {
+ k &= tablesize;
+ }
+ len = snprintf(buf, sizeof(buf), "%c\tk%d\tv%d\n", op, k, v);
+ }
+ const int wlen = write(fd.get(), buf, len);
+ if (wlen != len) {
+ return;
+ }
+ }
+ size_t read_cnt = 0;
+ size_t read_pos = 0;
+ while (read_cnt < arg.sh.pipe) {
+ const int rlen = read(fd.get(), buf + read_pos, sizeof(buf) - read_pos);
+ if (rlen <= 0) {
+ return;
+ }
+ read_pos += rlen;
+ while (true) {
+ const char *const p = static_cast<const char *>(memchr(buf, '\n',
+ read_pos));
+ if (p == 0) {
+ break;
+ }
+ ++read_cnt;
+ ++io_success_count;
+ arg.sh.increment_count();
+ if (p != buf && buf[0] == '=') {
+ ++op_success_count;
+ }
+ const size_t rest_size = buf + read_pos - (p + 1);
+ if (rest_size != 0) {
+ memmove(buf, p + 1, rest_size);
+ }
+ read_pos = rest_size;
+ }
+ }
+ }
+}
+
+void
+hstest_thread::test_2_3(int test_num)
+{
+#if 0
+ char buf_k[128], buf_v[128];
+ unsigned int seed = arg.id;
+ op_base_t op = static_cast<op_base_t>(arg.sh.op);
+ micli_ptr hnd;
+ if (test_num == 2) {
+ hnd = micli_i::create_remote(arg.sh.conf);
+ } else if (test_num == 3) {
+ // hnd = micli_i::create_inproc(arg.sh.localdb);
+ }
+ if (hnd.get() == 0) {
+ return;
+ }
+ for (size_t i = 0; i < arg.sh.loop; ++i) {
+ for (size_t j = 0; j < arg.sh.pipe; ++j) {
+ int k = 0, v = 0, klen = 0, vlen = 0;
+ k = rand_r(&seed);
+ klen = snprintf(buf_k, sizeof(buf_k), "k%d", k);
+ v = rand_r(&seed); /* unused */
+ vlen = snprintf(buf_v, sizeof(buf_v), "v%d", v);
+ string_ref arr[2];
+ arr[0] = string_ref(buf_k, klen);
+ arr[1] = string_ref(buf_v, vlen);
+ pstrarr_ptr rec(arr, 2);
+ if (hnd->execute(op, 0, 0, rec.get_const())) {
+ ++io_success_count;
+ arg.sh.increment_count();
+ const dataset& res = hnd->get_result_ref();
+ if (res.size() == 1) {
+ ++op_success_count;
+ }
+ }
+ }
+ }
+#endif
+}
+
+void
+hstest_thread::test_4_5(int test_num)
+{
+#if 0
+ char buf_k[128], buf_v[8192];
+ memset(buf_v, ' ', sizeof(buf_v));
+ unsigned int seed = arg.id;
+ op_base_t op = static_cast<op_base_t>(arg.sh.op);
+ micli_ptr hnd;
+ if (test_num == 4) {
+ hnd = micli_i::create_remote(arg.sh.conf);
+ } else if (test_num == 5) {
+ hnd = micli_i::create_inproc(arg.sh.localdb);
+ }
+ if (hnd.get() == 0) {
+ return;
+ }
+ for (size_t i = 0; i < arg.sh.loop; ++i) {
+ for (size_t j = 0; j < arg.sh.pipe; ++j) {
+ int k = 0, klen = 0, vlen = 0;
+ k = i & 0x0000ffffUL;
+ if (k == 0) {
+ fprintf(stderr, "k=0\n");
+ }
+ klen = snprintf(buf_k, sizeof(buf_k), "k%d", k);
+ vlen = rand_r(&seed) % 8192;
+ string_ref arr[2];
+ arr[0] = string_ref(buf_k, klen);
+ arr[1] = string_ref(buf_v, vlen);
+ pstrarr_ptr rec(arr, 2);
+ if (hnd->execute(op, 0, 0, rec.get_const())) {
+ ++io_success_count;
+ const dataset& res = hnd->get_result_ref();
+ if (res.size() == 1) {
+ ++op_success_count;
+ }
+ }
+ }
+ }
+#endif
+}
+
+void
+hstest_thread::test_6(int test_num)
+{
+ int count = arg.sh.conf.get_int("count", 1);
+ auto_file fds[count];
+ for (int i = 0; i < count; ++i) {
+ const double t1 = gettimeofday_double();
+ std::string err;
+ if (socket_connect(fds[i], arg.sh.arg, err) != 0) {
+ fprintf(stderr, "id=%zu i=%d err=%s\n", arg.id, i, err.c_str());
+ }
+ const double t2 = gettimeofday_double();
+ if (t2 - t1 > 1) {
+ fprintf(stderr, "id=%zu i=%d time %f\n", arg.id, i, t2 - t1);
+ }
+ }
+}
+
+void
+hstest_thread::test_7(int num)
+{
+ /*
+ set foo 0 0 10
+ 0123456789
+ STORED
+ get foo
+ VALUE foo 0 10
+ 0123456789
+ END
+ get var
+ END
+ */
+ char buf[1024];
+ const int keep_connection = arg.sh.conf.get_int("keep_connection", 1);
+ unsigned int seed = arg.id;
+ seed ^= arg.sh.conf.get_int("seed_xor", 0);
+ const int tablesize = arg.sh.conf.get_int("tablesize", 0);
+ const char op = arg.sh.op;
+ for (size_t i = 0; i < arg.sh.loop; ++i) {
+ const double tm1 = gettimeofday_double();
+ std::string err;
+ if (fd.get() < 0 && socket_connect(fd, arg.sh.arg, err) != 0) {
+ fprintf(stderr, "connect: %d %s\n", errno, strerror(errno));
+ return;
+ }
+ for (size_t j = 0; j < arg.sh.pipe; ++j) {
+ int k = 0, v = 0, len = 0;
+ if (op == 'G') {
+ k = rand_r(&seed);
+ v = rand_r(&seed); /* unused */
+ if (tablesize != 0) {
+ k &= tablesize;
+ }
+ len = snprintf(buf, sizeof(buf), "get k%d\r\n", k);
+ } else {
+ k = rand_r(&seed);
+ v = rand_r(&seed);
+ if (tablesize != 0) {
+ k &= tablesize;
+ }
+ char vbuf[1024];
+ int vlen = snprintf(vbuf, sizeof(vbuf),
+ "v%d"
+ // "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
+ // "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
+ // "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
+ // "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
+ // "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
+ , v);
+ len = snprintf(buf, sizeof(buf), "set k%d 0 0 %d\r\n%s\r\n",
+ k, vlen, vbuf);
+ }
+ const int wlen = write(fd.get(), buf, len);
+ if (wlen != len) {
+ return;
+ }
+ }
+ size_t read_cnt = 0;
+ size_t read_pos = 0;
+ bool read_response_done = false;
+ bool expect_value = false;
+ while (!read_response_done) {
+ const int rlen = read(fd.get(), buf + read_pos, sizeof(buf) - read_pos);
+ if (rlen <= 0) {
+ return;
+ }
+ read_pos += rlen;
+ while (true) {
+ const char *const p = static_cast<const char *>(memchr(buf, '\n',
+ read_pos));
+ if (p == 0) {
+ break;
+ }
+ ++read_cnt;
+ if (expect_value) {
+ expect_value = false;
+ } else if (p >= buf + 6 && memcmp(buf, "VALUE ", 6) == 0) {
+ expect_value = true;
+ ++op_success_count;
+ } else {
+ if (p == buf + 7 && memcmp(buf, "STORED\r", 7) == 0) {
+ ++op_success_count;
+ }
+ read_response_done = true;
+ }
+ const size_t rest_size = buf + read_pos - (p + 1);
+ if (rest_size != 0) {
+ memmove(buf, p + 1, rest_size);
+ }
+ read_pos = rest_size;
+ }
+ ++io_success_count;
+ }
+ arg.sh.increment_count();
+ if (!keep_connection) {
+ fd.close();
+ }
+ const double tm2 = gettimeofday_double();
+ set_timing(tm2 - tm1);
+ sleep_if();
+ }
+}
+
+struct rec {
+ std::string key;
+ std::string value;
+};
+
+void
+hstest_thread::test_8(int test_num)
+{
+#if 0
+ char buf_k[128], buf_v[128];
+ unsigned int seed = arg.id;
+ // op_base_t op = static_cast<op_base_t>(arg.sh.op);
+ using namespace boost::multi_index;
+ typedef member<rec, std::string, &rec::key> rec_get_key;
+ typedef ordered_unique<rec_get_key> oui;
+ typedef multi_index_container< rec, indexed_by<oui> > mic;
+ #if 0
+ typedef std::map<std::string, std::string> m_type;
+ m_type m;
+ #endif
+ mic m;
+ for (size_t i = 0; i < arg.sh.loop; ++i) {
+ for (size_t j = 0; j < arg.sh.pipe; ++j) {
+ int k = 0, v = 0, klen = 0, vlen = 0;
+ k = rand_r(&seed);
+ klen = snprintf(buf_k, sizeof(buf_k), "k%d", k);
+ v = rand_r(&seed); /* unused */
+ vlen = snprintf(buf_v, sizeof(buf_v), "v%d", v);
+ const std::string ks(buf_k, klen);
+ const std::string vs(buf_v, vlen);
+ rec r;
+ r.key = ks;
+ r.value = vs;
+ m.insert(r);
+ // m.insert(std::make_pair(ks, vs));
+ ++io_success_count;
+ ++op_success_count;
+ arg.sh.increment_count();
+ }
+ }
+#endif
+}
+
+struct mysqltest_thread_initobj : private noncopyable {
+ mysqltest_thread_initobj() {
+ mysql_thread_init();
+ }
+ ~mysqltest_thread_initobj() {
+ mysql_thread_end();
+ }
+};
+
+void
+hstest_thread::test_9(int test_num)
+{
+ /* create table hstest
+ * ( k varchar(255) not null, v varchar(255) not null, primary key(k))
+ * engine = innodb; */
+ auto_mysql db;
+ // mysqltest_thread_initobj initobj;
+ std::string err;
+ const char op = arg.sh.op;
+ const std::string suffix = arg.sh.conf.get_str("value_suffix", "upd");
+ unsigned long long err_cnt = 0;
+ unsigned long long query_cnt = 0;
+ #if 0
+ my_bool reconnect = 0;
+ if (mysql_options(db, MYSQL_OPT_RECONNECT, &reconnect) != 0) {
+ err = "mysql_options() failed";
+ ++err_cnt;
+ return;
+ }
+ #endif
+ unsigned int seed = time(0) + arg.id + 1;
+ seed ^= arg.sh.conf.get_int("seed_xor", 0);
+ drand48_data randbuf;
+ srand48_r(seed, &randbuf);
+ const std::string mysql_host = arg.sh.conf.get_str("host", "localhost");
+ const int mysql_port = arg.sh.conf.get_int("mysqlport", 3306);
+ const int num = arg.sh.loop;
+ const std::string mysql_user = arg.sh.conf.get_str("mysqluser", "root");
+ const std::string mysql_passwd = arg.sh.conf.get_str("mysqlpass", "");
+ const std::string mysql_dbname = arg.sh.conf.get_str("dbname", "hstest");
+ const int keep_connection = arg.sh.conf.get_int("keep_connection", 1);
+ const int verbose = arg.sh.conf.get_int("verbose", 1);
+ const int tablesize = arg.sh.conf.get_int("tablesize", 10000);
+ const int moreflds = arg.sh.conf.get_int("moreflds", 0);
+ const std::string moreflds_prefix = arg.sh.conf.get_str(
+ "moreflds_prefix", "column0123456789_");
+ const int use_handler = arg.sh.conf.get_int("handler", 0);
+ const int sched_flag = arg.sh.conf.get_int("sched", 0);
+ const int ssps = arg.sh.conf.get_int("ssps", 0);
+ std::string flds = "v";
+ for (int i = 0; i < moreflds; ++i) {
+ char buf[1024];
+ snprintf(buf, sizeof(buf), ",%s%d", moreflds_prefix.c_str(), i);
+ flds += std::string(buf);
+ }
+ int connected = 0;
+ std::auto_ptr<auto_mysql_stmt> stmt;
+ for (int i = 0; i < num; ++i) {
+ const double tm1 = gettimeofday_double();
+ const int flags = 0;
+ if (connected == 0) {
+ if (!mysql_real_connect(db, mysql_host.c_str(),
+ mysql_user.c_str(), mysql_user.empty() ? 0 : mysql_passwd.c_str(),
+ mysql_dbname.c_str(), mysql_port, 0, flags)) {
+ err = "failed to connect: " + std::string(mysql_error(db));
+ if (verbose >= 1) {
+ fprintf(stderr, "e=[%s]\n", err.c_str());
+ }
+ ++err_cnt;
+ return;
+ }
+ arg.sh.increment_conn(1);
+ }
+ int r = 0;
+ if (connected == 0 && use_handler) {
+ const char *const q = "handler hstest_table1 open";
+ r = mysql_real_query(db, q, strlen(q));
+ if (r != 0) {
+ err = 1;
+ }
+ }
+ if (connected == 0 && ssps) {
+ stmt.reset(new auto_mysql_stmt(db));
+ const char *const q = "select v from hstest_table1 where k = ?";
+ r = mysql_stmt_prepare(*stmt, q, strlen(q));
+ if (r != 0) {
+ fprintf(stderr, "ssps err\n");
+ ++err_cnt;
+ return;
+ }
+ }
+ connected = 1;
+ std::string result_str;
+ unsigned int err = 0;
+ unsigned int num_flds = 0, num_affected_rows = 0;
+ int got_data = 0;
+ char buf_query[16384];
+ int buf_query_len = 0;
+ int k = 0, v = 0;
+ {
+ double kf = 0, vf = 0;
+ drand48_r(&randbuf, &kf);
+ drand48_r(&randbuf, &vf);
+ k = int(kf * tablesize);
+ v = int(vf * tablesize);
+ #if 0
+ k = rand_r(&seed);
+ v = rand_r(&seed);
+ if (tablesize != 0) {
+ k %= tablesize;
+ }
+ #endif
+ if (op == 'G') {
+ if (use_handler) {
+ buf_query_len = snprintf(buf_query, sizeof(buf_query),
+ "handler hstest_table1 read `primary` = ( '%d' )", k);
+ // TODO: moreflds
+ } else if (ssps) {
+ //
+ } else {
+ buf_query_len = snprintf(buf_query, sizeof(buf_query),
+ "select %s from hstest_table1 where k = '%d'", flds.c_str(), k);
+ }
+ } else if (op == 'U') {
+ buf_query_len = snprintf(buf_query, sizeof(buf_query),
+ "update hstest_table1 set v = '%d_%d%s' where k = '%d'",
+ v, k, suffix.c_str(), k);
+ } else if (op == 'R') {
+ buf_query_len = snprintf(buf_query, sizeof(buf_query),
+ "replace into hstest_table1 values ('%d', 'v%d')", k, v);
+ // TODO: moreflds
+ }
+ }
+ if (r == 0) {
+ if (ssps) {
+ MYSQL_BIND bind[1] = { };
+ bind[0].buffer_type = MYSQL_TYPE_LONG;
+ bind[0].buffer = (char *)&k;
+ bind[0].is_null = 0;
+ bind[0].length = 0;
+ if (mysql_stmt_bind_param(*stmt, bind)) {
+ fprintf(stderr, "err: %s\n", mysql_stmt_error(*stmt));
+ ++err_cnt;
+ return;
+ }
+ r = mysql_stmt_execute(*stmt);
+ // fprintf(stderr, "stmt exec\n");
+ } else {
+ r = mysql_real_query(db, buf_query, buf_query_len);
+ // fprintf(stderr, "real query\n");
+ }
+ ++query_cnt;
+ }
+ if (r != 0) {
+ err = 1;
+ } else if (ssps) {
+ if (verbose >= 0) {
+ char resbuf[1024];
+ unsigned long res_len = 0;
+ MYSQL_BIND bind[1] = { };
+ bind[0].buffer_type = MYSQL_TYPE_STRING;
+ bind[0].buffer = resbuf;
+ bind[0].buffer_length = sizeof(resbuf);
+ bind[0].length = &res_len;
+ if (mysql_stmt_bind_result(*stmt, bind)) {
+ fprintf(stderr, "err: %s\n", mysql_stmt_error(*stmt));
+ ++err_cnt;
+ return;
+ }
+ if (mysql_stmt_fetch(*stmt)) {
+ fprintf(stderr, "err: %s\n", mysql_stmt_error(*stmt));
+ ++err_cnt;
+ return;
+ }
+ if (!result_str.empty()) {
+ result_str += " ";
+ }
+ result_str += std::string(resbuf, res_len);
+ // fprintf(stderr, "SSPS RES: %s\n", result_str.c_str());
+ got_data = 1;
+ } else {
+ got_data = 1;
+ }
+ } else {
+ auto_mysql_res res(db);
+ if (res != 0) {
+ if (verbose >= 0) {
+ num_flds = mysql_num_fields(res);
+ MYSQL_ROW row = 0;
+ while ((row = mysql_fetch_row(res)) != 0) {
+ got_data = 1;
+ unsigned long *const lengths = mysql_fetch_lengths(res);
+ if (verbose >= 2) {
+ for (unsigned int i = 0; i < num_flds; ++i) {
+ if (!result_str.empty()) {
+ result_str += " ";
+ }
+ result_str += std::string(row[i], lengths[i]);
+ }
+ }
+ }
+ } else {
+ got_data = 1;
+ }
+ } else {
+ if (mysql_field_count(db) == 0) {
+ num_affected_rows = mysql_affected_rows(db);
+ } else {
+ err = 1;
+ }
+ }
+ }
+ if (verbose >= 2 || (verbose >= 1 && err != 0)) {
+ if (err) {
+ ++err_cnt;
+ const char *const errstr = mysql_error(db);
+ fprintf(stderr, "e=[%s] a=%u q=[%s]\n", errstr,
+ num_affected_rows, buf_query);
+ } else {
+ fprintf(stderr, "a=%u q=[%s] r=[%s]\n", num_affected_rows, buf_query,
+ result_str.c_str());
+ }
+ }
+ if (err == 0) {
+ ++io_success_count;
+ if (num_affected_rows > 0 || got_data > 0) {
+ ++op_success_count;
+ } else {
+ if (verbose >= 1) {
+ fprintf(stderr, "k=%d numaff=%u gotdata=%d\n",
+ k, num_affected_rows, got_data);
+ }
+ }
+ arg.sh.increment_count();
+ }
+ if (!keep_connection) {
+ if (stmt.get() != 0) {
+ stmt.reset();
+ }
+ db.reset();
+ connected = 0;
+ }
+ const double tm2 = gettimeofday_double();
+ set_timing(tm2 - tm1);
+ sleep_if();
+ if (sched_flag) {
+ sched_yield();
+ }
+ }
+ if (verbose >= 1) {
+ fprintf(stderr, "thread finished (error_count=%llu)\n", err_cnt);
+ }
+}
+
+void
+hstest_thread::test_10(int test_num)
+{
+ const int keep_connection = arg.sh.conf.get_int("keep_connection", 1);
+ unsigned int seed = time(0) + arg.id + 1;
+ seed ^= arg.sh.conf.get_int("seed_xor", 0);
+ drand48_data randbuf;
+ srand48_r(seed, &randbuf);
+ std::string err;
+ int keepconn_count = 0;
+ const char op = arg.sh.op;
+ const int verbose = arg.sh.conf.get_int("verbose", 1);
+ const std::string suffix = arg.sh.conf.get_str("value_suffix", "upd");
+ const int tablesize = arg.sh.conf.get_int("tablesize", 10000);
+ const int firstkey = arg.sh.conf.get_int("firstkey", 0);
+ const int sched_flag = arg.sh.conf.get_int("sched", 0);
+ const int moreflds = arg.sh.conf.get_int("moreflds", 0);
+ const std::string dbname = arg.sh.conf.get_str("dbname", "hstest");
+ const std::string table = arg.sh.conf.get_str("table", "hstest_table1");
+ const std::string index = arg.sh.conf.get_str("index", "PRIMARY");
+ const std::string field = arg.sh.conf.get_str("field", "v");
+ const std::string moreflds_prefix = arg.sh.conf.get_str(
+ "moreflds_prefix", "column0123456789_");
+ const int dump = arg.sh.dump;
+ const int nodup = arg.sh.conf.get_int("nodup", 0);
+ std::string moreflds_str;
+ for (int i = 0; i < moreflds; ++i) {
+ char sbuf[1024];
+ snprintf(sbuf, sizeof(sbuf), ",%s%d", moreflds_prefix.c_str(), i);
+ moreflds_str += std::string(sbuf);
+ }
+ char wbuf[16384], rbuf[16384];
+ int wbuflen = 0;
+ for (size_t i = 0; i < arg.sh.loop; ++i) {
+ int len = 0, rlen = 0, wlen = 0;
+ #if 0
+ const double tm1 = gettimeofday_double();
+ #endif
+ if (fd.get() < 0) {
+ if (socket_connect(fd, arg.sh.arg, err) != 0) {
+ fprintf(stderr, "connect: %d %s\n", errno, strerror(errno));
+ return;
+ }
+ len = snprintf(wbuf, sizeof(wbuf),
+ "P\t1\t%s\t%s\tPRIMARY\t%s%s\n", dbname.c_str(), table.c_str(),
+ field.c_str(), moreflds_str.c_str());
+ /* pst_num, db, table, index, retflds */
+ wlen = write(fd.get(), wbuf, len);
+ if (len != wlen) {
+ fprintf(stderr, "write: %d %d\n", len, wlen);
+ return;
+ }
+ rlen = read(fd.get(), rbuf, sizeof(rbuf));
+ if (rlen <= 0 || rbuf[rlen - 1] != '\n') {
+ fprintf(stderr, "read: rlen=%d errno=%d\n", rlen, errno);
+ return;
+ }
+ if (rbuf[0] != '0') {
+ fprintf(stderr, "failed to open table\n");
+ return;
+ }
+ arg.sh.increment_conn(1);
+ }
+ const double tm1 = gettimeofday_double();
+ for (size_t j = 0; j < arg.sh.pipe; ++j) {
+ int k = 0, v = 0;
+ wbuflen = 0;
+ {
+ while (true) {
+ double kf = 0, vf = 0;
+ drand48_r(&randbuf, &kf);
+ drand48_r(&randbuf, &vf);
+ k = int(kf * tablesize) + firstkey;
+ v = int(vf * tablesize) + firstkey;
+ // k = rand_r(&seed);
+ // v = rand_r(&seed); /* unused */
+ #if 0
+ if (tablesize != 0) {
+ k &= tablesize;
+ }
+ #endif
+ if (op == 'G') {
+ wbuflen = snprintf(wbuf, sizeof(wbuf), "1\t=\t1\t%d\n", k);
+ } else if (op == 'U') {
+ wbuflen = snprintf(wbuf, sizeof(wbuf),
+ "1\t=\t1\t%d\t1\t0\tU\t%d_%d%s\n", k, v, k, suffix.c_str());
+ }
+ if (k - firstkey < arg.sh.keygen_size) {
+ volatile char *const ptr = arg.sh.keygen + (k - firstkey);
+ // int oldv = __sync_fetch_and_or(ptr, 1);
+ int oldv = *ptr;
+ *ptr += 1;
+ if (nodup && oldv != 0) {
+ if (dump) {
+ fprintf(stderr, "retry\n");
+ }
+ continue;
+ }
+ } else {
+ if (nodup) {
+ if (dump) {
+ fprintf(stderr, "retry2\n");
+ }
+ continue;
+ }
+ }
+ break;
+ }
+ }
+ wlen = write(fd.get(), wbuf, wbuflen);
+ if (wlen != wbuflen) {
+ fprintf(stderr, "write: %d %d\n", wbuflen, wlen);
+ return;
+ }
+ }
+ size_t read_cnt = 0;
+ size_t read_pos = 0;
+ while (read_cnt < arg.sh.pipe) {
+ rlen = read(fd.get(), rbuf + read_pos, sizeof(rbuf) - read_pos);
+ if (rlen <= 0) {
+ fprintf(stderr, "read: %d\n", rlen);
+ return;
+ }
+ read_pos += rlen;
+ while (true) {
+ const char *const nl = static_cast<const char *>(memchr(rbuf, '\n',
+ read_pos));
+ if (nl == 0) {
+ break;
+ }
+ ++read_cnt;
+ ++io_success_count;
+ const char *t1 = static_cast<const char *>(memchr(rbuf, '\t',
+ nl - rbuf));
+ if (t1 == 0) {
+ fprintf(stderr, "error \n");
+ break;
+ }
+ ++t1;
+ const char *t2 = static_cast<const char *>(memchr(t1, '\t',
+ nl - t1));
+ if (t2 == 0) {
+ if (verbose > 1) {
+ fprintf(stderr, "key: notfound \n");
+ }
+ break;
+ }
+ ++t2;
+ if (t1 == rbuf + 2 && rbuf[0] == '0') {
+ if (op == 'G') {
+ ++op_success_count;
+ arg.sh.increment_count();
+ } else if (op == 'U') {
+ const char *t3 = t2;
+ while (t3 != nl && t3[0] >= 0x10) {
+ ++t3;
+ }
+ if (t3 != t2 + 1 || t2[0] != '1') {
+ const std::string mess(t2, t3);
+ fprintf(stderr, "mod: %s\n", mess.c_str());
+ } else {
+ ++op_success_count;
+ arg.sh.increment_count();
+ if (arg.sh.dump && arg.sh.pipe == 1) {
+ fwrite(wbuf, wbuflen, 1, stderr);
+ }
+ }
+ }
+ } else {
+ const char *t3 = t2;
+ while (t3 != nl && t3[0] >= 0x10) {
+ ++t3;
+ }
+ const std::string mess(t2, t3);
+ fprintf(stderr, "err: %s\n", mess.c_str());
+ }
+ const size_t rest_size = rbuf + read_pos - (nl + 1);
+ if (rest_size != 0) {
+ memmove(rbuf, nl + 1, rest_size);
+ }
+ read_pos = rest_size;
+ }
+ }
+ if (!keep_connection) {
+ fd.reset();
+ arg.sh.increment_conn(-1);
+ } else if (keep_connection > 1 && ++keepconn_count > keep_connection) {
+ keepconn_count = 0;
+ fd.reset();
+ arg.sh.increment_conn(-1);
+ }
+ const double tm2 = gettimeofday_double();
+ set_timing(tm2 - tm1);
+ sleep_if();
+ if (sched_flag) {
+ sched_yield();
+ }
+ }
+ if (dump) {
+ fprintf(stderr, "done\n");
+ }
+}
+
+void
+hstest_thread::sleep_if()
+{
+ if (arg.sh.usleep) {
+ struct timespec ts = {
+ arg.sh.usleep / 1000000,
+ (arg.sh.usleep % 1000000) * 1000
+ };
+ nanosleep(&ts, 0);
+ }
+}
+
+void
+hstest_thread::set_timing(double time_spent)
+{
+ response_min = std::min(response_min, time_spent);
+ response_max = std::max(response_max, time_spent);
+ response_sum += time_spent;
+ if (op_success_count != 0) {
+ response_avg = response_sum / op_success_count;
+ }
+}
+
+void
+hstest_thread::test_11(int test_num)
+{
+ const int keep_connection = arg.sh.conf.get_int("keep_connection", 1);
+ const int tablesize = arg.sh.conf.get_int("tablesize", 0);
+ unsigned int seed = arg.id;
+ seed ^= arg.sh.conf.get_int("seed_xor", 0);
+ std::string err;
+ hstcpcli_ptr cli;
+ for (size_t i = 0; i < arg.sh.loop; ++i) {
+ if (cli.get() == 0) {
+ cli = hstcpcli_i::create(arg.sh.arg);
+ cli->request_buf_open_index(0, "hstest", "hstest_table1", "", "v");
+ /* pst_num, db, table, index, retflds */
+ if (cli->request_send() != 0) {
+ fprintf(stderr, "reuqest_send: %s\n", cli->get_error().c_str());
+ return;
+ }
+ size_t num_flds = 0;
+ if (cli->response_recv(num_flds) != 0) {
+ fprintf(stderr, "reuqest_recv: %s\n", cli->get_error().c_str());
+ return;
+ }
+ cli->response_buf_remove();
+ }
+ for (size_t j = 0; j < arg.sh.pipe; ++j) {
+ char buf[256];
+ int k = 0, v = 0, len = 0;
+ {
+ k = rand_r(&seed);
+ v = rand_r(&seed); /* unused */
+ if (tablesize != 0) {
+ k &= tablesize;
+ }
+ len = snprintf(buf, sizeof(buf), "%d", k);
+ }
+ const string_ref key(buf, len);
+ const string_ref op("=", 1);
+ cli->request_buf_exec_generic(0, op, &key, 1, 1, 0, string_ref(), 0, 0);
+ }
+ if (cli->request_send() != 0) {
+ fprintf(stderr, "reuqest_send: %s\n", cli->get_error().c_str());
+ return;
+ }
+ size_t read_cnt = 0;
+ for (size_t j = 0; j < arg.sh.pipe; ++j) {
+ size_t num_flds = 0;
+ if (cli->response_recv(num_flds) != 0) {
+ fprintf(stderr, "reuqest_recv: %s\n", cli->get_error().c_str());
+ return;
+ }
+ {
+ ++read_cnt;
+ ++io_success_count;
+ arg.sh.increment_count();
+ {
+ ++op_success_count;
+ }
+ }
+ cli->response_buf_remove();
+ }
+ if (!keep_connection) {
+ cli.reset();
+ }
+ }
+}
+
+void
+hstest_thread::test_watch()
+{
+ const int timelimit = arg.sh.conf.get_int("timelimit", 0);
+ const int timelimit_offset = timelimit / 2;
+ int loop = 0;
+ double t1 = 0, t2 = 0;
+ size_t cnt_t1 = 0, cnt_t2 = 0;
+ size_t prev_cnt = 0;
+ double now_f = 0;
+ while (true) {
+ sleep(1);
+ const size_t cnt = arg.sh.count;
+ const size_t df = cnt - prev_cnt;
+ prev_cnt = cnt;
+ const double now_prev = now_f;
+ now_f = gettimeofday_double();
+ if (now_prev != 0) {
+ const double rps = static_cast<double>(df) / (now_f - now_prev);
+ fprintf(stderr, "now: %zu cntdiff: %zu tdiff: %f rps: %f\n",
+ static_cast<size_t>(now_f), df, now_f - now_prev, rps);
+ }
+ if (timelimit != 0) {
+ if (arg.sh.wait_conn == 0 || arg.sh.conn_count >= arg.sh.wait_conn) {
+ ++loop;
+ }
+ if (loop == timelimit_offset) {
+ t1 = gettimeofday_double();
+ cnt_t1 = cnt;
+ arg.sh.enable_timing = 1;
+ fprintf(stderr, "start timing\n");
+ } else if (loop == timelimit_offset + timelimit) {
+ t2 = gettimeofday_double();
+ cnt_t2 = cnt;
+ const size_t cnt_diff = cnt_t2 - cnt_t1;
+ const double tdiff = t2 - t1;
+ const double qps = cnt_diff / (tdiff != 0 ? tdiff : 1);
+ fprintf(stderr, "(%f: %zu, %f: %zu), %10.5f qps\n",
+ t1, cnt_t1, t2, cnt_t2, qps);
+ size_t keycnt = 0;
+ for (int i = 0; i < arg.sh.keygen_size; ++i) {
+ if (arg.sh.keygen[i]) {
+ ++keycnt;
+ }
+ }
+ fprintf(stderr, "keygen=%zu\n", keycnt);
+ break;
+ }
+ }
+ }
+#if 0
+ int loop = 0;
+ double t1 = 0, t2 = 0;
+ size_t cnt_t1 = 0, cnt_t2 = 0;
+ size_t prev_cnt = 0;
+ while (true) {
+ sleep(1);
+ const size_t cnt = arg.sh.count;
+ const size_t df = cnt - prev_cnt;
+ prev_cnt = cnt;
+ const size_t now = time(0);
+ fprintf(stderr, "%zu %zu\n", now, df);
+ if (timelimit != 0) {
+ ++loop;
+ if (loop == timelimit_offset) {
+ t1 = gettimeofday_double();
+ cnt_t1 = cnt;
+ } else if (loop == timelimit_offset + timelimit) {
+ t2 = gettimeofday_double();
+ cnt_t2 = cnt;
+ const size_t cnt_diff = cnt_t2 - cnt_t1;
+ const double tdiff = t2 - t1;
+ const double qps = cnt_diff / (tdiff != 0 ? tdiff : 1);
+ fprintf(stderr, "(%f: %zu, %f: %zu), %10.5f qps\n",
+ t1, cnt_t1, t2, cnt_t2, qps);
+ size_t keycnt = 0;
+ for (int i = 0; i < arg.sh.keygen_size; ++i) {
+ if (arg.sh.keygen[i]) {
+ ++keycnt;
+ }
+ }
+ fprintf(stderr, "keygen=%zu\n", keycnt);
+ _exit(0);
+ }
+ }
+ }
+#endif
+}
+
+void
+hstest_thread::test_12(int test_num)
+{
+ /* NOTE: num_threads should be 1 */
+ /* create table hstest
+ * ( k varchar(255) not null, v varchar(255) not null, primary key(k))
+ * engine = innodb; */
+ mysqltest_thread_initobj initobj;
+ auto_mysql db;
+ std::string err;
+ unsigned long long err_cnt = 0;
+ unsigned long long query_cnt = 0;
+ #if 0
+ my_bool reconnect = 0;
+ if (mysql_options(db, MYSQL_OPT_RECONNECT, &reconnect) != 0) {
+ err = "mysql_options() failed";
+ ++err_cnt;
+ return;
+ }
+ #endif
+ const std::string mysql_host = arg.sh.conf.get_str("host", "localhost");
+ const int mysql_port = arg.sh.conf.get_int("mysqlport", 3306);
+ const unsigned int num = arg.sh.loop;
+ const size_t pipe = arg.sh.pipe;
+ const std::string mysql_user = arg.sh.conf.get_str("mysqluser", "root");
+ const std::string mysql_passwd = arg.sh.conf.get_str("mysqlpass", "");
+ const std::string mysql_dbname = arg.sh.conf.get_str("db", "hstest");
+ const int keep_connection = arg.sh.conf.get_int("keep_connection", 1);
+ const int verbose = arg.sh.conf.get_int("verbose", 1);
+ const int use_handler = arg.sh.conf.get_int("handler", 0);
+ int connected = 0;
+ unsigned int k = 0;
+ string_buffer buf;
+ for (unsigned int i = 0; i < num; ++i) {
+ const int flags = 0;
+ if (connected == 0 && !mysql_real_connect(db, mysql_host.c_str(),
+ mysql_user.c_str(), mysql_user.empty() ? 0 : mysql_passwd.c_str(),
+ mysql_dbname.c_str(), mysql_port, 0, flags)) {
+ err = "failed to connect: " + std::string(mysql_error(db));
+ if (verbose >= 1) {
+ fprintf(stderr, "e=[%s]\n", err.c_str());
+ }
+ ++err_cnt;
+ return;
+ }
+ int r = 0;
+ if (connected == 0 && use_handler) {
+ const char *const q = "handler hstest open";
+ r = mysql_real_query(db, q, strlen(q));
+ if (r != 0) {
+ err = 1;
+ }
+ }
+ connected = 1;
+ std::string result_str;
+ unsigned int err = 0;
+ unsigned int num_flds = 0, num_affected_rows = 0;
+ int got_data = 0;
+ buf.clear();
+ buf.append_literal("insert into hstest values ");
+ for (size_t j = 0; j < pipe; ++j) {
+ const unsigned int v = ~k;
+ if (j != 0) {
+ buf.append_literal(",");
+ }
+ char *wp = buf.make_space(64);
+ int buf_query_len = snprintf(wp, 64, "('k%u', 'v%u')", k, v);
+ buf.space_wrote(buf_query_len);
+ ++k;
+ }
+ if (r == 0) {
+ r = mysql_real_query(db, buf.begin(), buf.size());
+ ++query_cnt;
+ }
+ if (r != 0) {
+ err = 1;
+ } else {
+ auto_mysql_res res(db);
+ if (res != 0) {
+ if (verbose >= 0) {
+ num_flds = mysql_num_fields(res);
+ MYSQL_ROW row = 0;
+ while ((row = mysql_fetch_row(res)) != 0) {
+ got_data = 1;
+ unsigned long *const lengths = mysql_fetch_lengths(res);
+ if (verbose >= 2) {
+ for (unsigned int i = 0; i < num_flds; ++i) {
+ if (!result_str.empty()) {
+ result_str += " ";
+ }
+ result_str += std::string(row[i], lengths[i]);
+ }
+ }
+ }
+ }
+ } else {
+ if (mysql_field_count(db) == 0) {
+ num_affected_rows = mysql_affected_rows(db);
+ } else {
+ err = 1;
+ }
+ }
+ }
+ if (verbose >= 2 || (verbose >= 1 && err != 0)) {
+ if (err) {
+ ++err_cnt;
+ const char *const errstr = mysql_error(db);
+ fprintf(stderr, "e=[%s] a=%u q=[%s]\n", errstr,
+ num_affected_rows, std::string(buf.begin(), buf.size()).c_str());
+ } else {
+ fprintf(stderr, "a=%u q=[%s] r=[%s]\n", num_affected_rows,
+ std::string(buf.begin(), buf.size()).c_str(),
+ result_str.c_str());
+ }
+ }
+ if (err == 0) {
+ ++io_success_count;
+ if (num_affected_rows > 0 || got_data > 0) {
+ ++op_success_count;
+ }
+ arg.sh.increment_count(pipe);
+ }
+ if (!keep_connection) {
+ db.reset();
+ connected = 0;
+ }
+ }
+ if (verbose >= 1) {
+ fprintf(stderr, "thread finished (error_count=%llu)\n", err_cnt);
+ }
+}
+
+void
+hstest_thread::test_21(int num)
+{
+ /* fsync test */
+ unsigned int id = arg.id;
+ std::string err;
+ #if 0
+ if (socket_connect(fd, arg.sh.arg, err) != 0) {
+ fprintf(stderr, "connect: %d %s\n", errno, strerror(errno));
+ return;
+ }
+ #endif
+ auto_file logfd;
+ char fname[1024];
+ snprintf(fname, sizeof(fname), "synctest_%u", id);
+ int open_flags = O_WRONLY | O_CREAT | O_TRUNC | O_APPEND;
+ logfd.reset(open(fname, open_flags, 0644));
+ if (logfd.get() < 0) {
+ fprintf(stderr, "open: %s: %d %s\n", fname, errno, strerror(errno));
+ return;
+ }
+ char buf[1024];
+ unsigned long long count = 0;
+ while (true) {
+ snprintf(buf, sizeof(buf), "%u %llu\n", id, count);
+ const size_t len = strlen(buf);
+ if (write(logfd.get(), buf, len) != (ssize_t)len) {
+ fprintf(stderr, "write: %s: %d %s\n", fname, errno, strerror(errno));
+ return;
+ }
+ #if 0
+ if (write(fd.get(), buf, len) != (ssize_t)len) {
+ fprintf(stderr, "write(sock): %d %s\n", errno, strerror(errno));
+ return;
+ }
+ #endif
+ if (fdatasync(logfd.get()) != 0) {
+ fprintf(stderr, "fsync: %s: %d %s\n", fname, errno, strerror(errno));
+ return;
+ }
+ ++count;
+ ++op_success_count;
+ arg.sh.increment_count();
+ }
+}
+
+void
+hstest_thread::test_22(int num)
+{
+ /* dd if=/dev/zero of=dummy.dat bs=1024M count=100 */
+ unsigned int id = arg.id;
+ std::string err;
+ auto_file filefd;
+ char fname[1024];
+ snprintf(fname, sizeof(fname), "dummy.dat");
+ int open_flags = O_RDONLY | O_DIRECT;
+ filefd.reset(open(fname, open_flags, 0644));
+ if (filefd.get() < 0) {
+ fprintf(stderr, "open: %s: %d %s\n", fname, errno, strerror(errno));
+ return;
+ }
+ char buf_x[4096 * 2];
+ char *const buf = (char *)(size_t(buf_x + 4096) / 4096 * 4096);
+ unsigned long long count = 0;
+ drand48_data randbuf;
+ unsigned long long seed = time(0);
+ seed *= 10;
+ seed += id;
+ srand48_r(seed, &randbuf);
+ for (unsigned int i = 0; i < arg.sh.loop; ++i) {
+ double kf = 0;
+ drand48_r(&randbuf, &kf);
+ kf *= (209715200 / 1);
+ // fprintf(stderr, "v=%f\n", kf);
+ off_t v = static_cast<off_t>(kf);
+ v %= (209715200 / 1);
+ v *= (512 * 1);
+ const double tm1 = gettimeofday_double();
+ const ssize_t r = pread(filefd.get(), buf, (512 * 1), v);
+ const double tm2 = gettimeofday_double();
+ if (r < 0) {
+ fprintf(stderr, "pread: %s: %d %s\n", fname, errno, strerror(errno));
+ return;
+ }
+ ++count;
+ ++op_success_count;
+ arg.sh.increment_count();
+ set_timing(tm2 - tm1);
+ }
+}
+
+void
+hstest_thread::operator ()()
+{
+ if (arg.watch_flag) {
+ return test_watch();
+ }
+ int test_num = arg.sh.conf.get_int("test", 1);
+ if (test_num == 1) {
+ test_1();
+ } else if (test_num == 2 || test_num == 3) {
+ test_2_3(test_num);
+ } else if (test_num == 4 || test_num == 5) {
+ test_4_5(test_num);
+ } else if (test_num == 6) {
+ test_6(test_num);
+ } else if (test_num == 7) {
+ test_7(test_num);
+ } else if (test_num == 8) {
+ test_8(test_num);
+ } else if (test_num == 9) {
+ test_9(test_num);
+ } else if (test_num == 10) {
+ test_10(test_num);
+ } else if (test_num == 11) {
+ test_11(test_num);
+ } else if (test_num == 12) {
+ test_12(test_num);
+ } else if (test_num == 21) {
+ test_21(test_num);
+ } else if (test_num == 22) {
+ test_22(test_num);
+ }
+ const int halt = arg.sh.conf.get_int("halt", 0);
+ if (halt) {
+ fprintf(stderr, "thread halted\n");
+ while (true) {
+ sleep(100000);
+ }
+ }
+ fprintf(stderr, "thread finished\n");
+}
+
+int
+hstest_main(int argc, char **argv)
+{
+ ignore_sigpipe();
+ hstest_shared shared;
+ parse_args(argc, argv, shared.conf);
+ shared.conf["port"] = shared.conf["hsport"];
+ shared.arg.set(shared.conf);
+ shared.loop = shared.conf.get_int("num", 1000);
+ shared.pipe = shared.conf.get_int("pipe", 1);
+ shared.verbose = shared.conf.get_int("verbose", 1);
+ const int tablesize = shared.conf.get_int("tablesize", 0);
+ std::vector<char> keygen(tablesize);
+ shared.keygen = &keygen[0];
+ shared.keygen_size = tablesize;
+ shared.usleep = shared.conf.get_int("usleep", 0);
+ shared.dump = shared.conf.get_int("dump", 0);
+ shared.num_threads = shared.conf.get_int("num_threads", 10);
+ shared.wait_conn = shared.conf.get_int("wait_conn", 0);
+ const std::string op = shared.conf.get_str("op", "G");
+ if (op.size() > 0) {
+ shared.op = op[0];
+ }
+ #if 0
+ const int localdb_flag = shared.conf.get_int("local", 0);
+ if (localdb_flag) {
+ shared.localdb = database_i::create(shared.conf);
+ }
+ #endif
+ const int num_thrs = shared.num_threads;
+ typedef thread<hstest_thread> thread_type;
+ typedef std::auto_ptr<thread_type> thread_ptr;
+ typedef auto_ptrcontainer< std::vector<thread_type *> > thrs_type;
+ thrs_type thrs;
+ for (int i = 0; i < num_thrs; ++i) {
+ const hstest_thread::arg_type arg(i, shared, false);
+ thread_ptr thr(new thread<hstest_thread>(arg));
+ thrs.push_back_ptr(thr);
+ }
+ for (size_t i = 0; i < thrs.size(); ++i) {
+ thrs[i]->start();
+ }
+ thread_ptr watch_thread;
+ const int timelimit = shared.conf.get_int("timelimit", 0);
+ {
+ const hstest_thread::arg_type arg(0, shared, true);
+ watch_thread = thread_ptr(new thread<hstest_thread>(arg));
+ watch_thread->start();
+ }
+ size_t iocnt = 0, opcnt = 0;
+ double respmin = 999999, respmax = 0;
+ double respsum = 0;
+ if (timelimit != 0) {
+ watch_thread->join();
+ }
+ for (size_t i = 0; i < thrs.size(); ++i) {
+ if (timelimit == 0) {
+ thrs[i]->join();
+ }
+ iocnt += (*thrs[i])->io_success_count;
+ opcnt += (*thrs[i])->op_success_count;
+ respmin = std::min(respmin, (*thrs[i])->response_min);
+ respmax = std::max(respmax, (*thrs[i])->response_max);
+ respsum += (*thrs[i])->response_sum;
+ }
+ fprintf(stderr, "io_success_count=%zu op_success_count=%zu\n", iocnt, opcnt);
+ fprintf(stderr, "respmin=%f respmax=%f respsum=%f respavg=%f\n",
+ respmin, respmax, respsum, respsum / opcnt);
+ size_t keycnt = 0;
+ for (size_t i = 0; i < keygen.size(); ++i) {
+ if (keygen[i]) {
+ ++keycnt;
+ }
+ }
+ fprintf(stderr, "keycnt=%zu\n", keycnt);
+ _exit(0);
+ return 0;
+}
+
+};
+
+int
+main(int argc, char **argv)
+{
+ return dena::hstest_main(argc, argv);
+}
+
diff --git a/plugin/handler_socket/client/hstest.pl b/plugin/handler_socket/client/hstest.pl
new file mode 100755
index 00000000000..4d177b6cdc8
--- /dev/null
+++ b/plugin/handler_socket/client/hstest.pl
@@ -0,0 +1,228 @@
+#!/usr/bin/perl
+
+# vim:sw=8:ai:ts=8
+
+use strict;
+use warnings;
+
+use DBI;
+use Net::HandlerSocket;
+
+my %conf = ();
+for my $i (@ARGV) {
+ my ($k, $v) = split(/=/, $i);
+ $conf{$k} = $v;
+}
+
+my $verbose = get_conf("verbose", 0);
+my $actions_str = get_conf("actions", "hsread");
+my $tablesize = get_conf("tablesize", 10000);
+my $db = get_conf("db", "hstest");
+my $table = get_conf("table", "hstest_table1");
+my $engine = get_conf("engine", "innodb");
+my $host = get_conf("host", "localhost");
+my $mysqlport = get_conf("mysqlport", 3306);
+my $mysqluser = get_conf("mysqluser", "root");
+my $mysqlpass = get_conf("mysqlpass", "");
+my $hsport = get_conf("hsport", 9999);
+my $loop = get_conf("loop", 10000);
+my $op = get_conf("op", "=");
+my $ssps = get_conf("ssps", 0);
+my $num_moreflds = get_conf("moreflds", 0);
+my $moreflds_prefix = get_conf("moreflds_prefix", "column0123456789_");
+my $keytype = get_conf("keytype", "varchar(32)");
+my $file = get_conf("file", undef);
+
+my $dsn = "DBI:mysql:database=;host=$host;port=$mysqlport"
+ . ";mysql_server_prepare=$ssps";
+my $dbh = DBI->connect($dsn, $mysqluser, $mysqlpass, { RaiseError => 1 });
+my $hsargs = { 'host' => $host, 'port' => $hsport };
+my $cli = new Net::HandlerSocket($hsargs);
+
+my @actions = split(/,/, $actions_str);
+for my $action (@actions) {
+ if ($action eq "table") {
+ print("TABLE $db.$table\n");
+ $dbh->do("drop database if exists $db");
+ $dbh->do("create database $db");
+ $dbh->do("use $db");
+ my $moreflds = get_createtbl_moreflds_str();
+ $dbh->do(
+ "create table $table (" .
+ "k $keytype primary key" .
+ ",v varchar(32) not null" .
+ $moreflds .
+ ") character set utf8 collate utf8_bin " .
+ "engine = $engine");
+ } elsif ($action eq "insert") {
+ print("INSERT $db.$table tablesize=$tablesize\n");
+ $dbh->do("use $db");
+ my $moreflds = get_insert_moreflds_str();
+ for (my $i = 0; $i < $tablesize; $i += 100) {
+ my $qstr = "insert into $db.$table values";
+ for (my $j = 0; $j < 100; ++$j) {
+ if ($j != 0) {
+ $qstr .= ",";
+ }
+ my $k = "" . ($i + $j);
+ my $v = "v" . int(rand(1000)) . ($i + $j);
+ $qstr .= "('$k', '$v'";
+ for (my $j = 0; $j < $num_moreflds; ++$j) {
+ $qstr .= ",'$j'";
+ }
+ $qstr .= ")";
+ }
+ $dbh->do($qstr);
+ print "$i/$tablesize\n" if $i % 1000 == 0;
+ }
+ } elsif ($action eq "read") {
+ print("READ $db.$table op=$op loop=$loop\n");
+ $dbh->do("use $db");
+ my $moreflds = get_select_moreflds_str();
+ my $sth = $dbh->prepare(
+ "select k,v$moreflds from $db.$table where k = ?");
+ for (my $i = 0; $i < $loop; ++$i) {
+ my $k = "" . int(rand($tablesize));
+ # print "k=$k\n";
+ $sth->execute($k);
+ if ($verbose >= 10) {
+ print "RET:";
+ while (my $ref = $sth->fetchrow_arrayref()) {
+ my $rk = $ref->[0];
+ my $rv = $ref->[1];
+ print " $rk $rv";
+ }
+ print "\n";
+ }
+ print "$i/$loop\n" if $i % 1000 == 0;
+ }
+ } elsif ($action eq "hsinsert") {
+ print("HSINSERT $db.$table tablesize=$tablesize\n");
+ $cli->open_index(1, $db, $table, '', 'k,v');
+ for (my $i = 0; $i < $tablesize; ++$i) {
+ my $k = "" . $i;
+ my $v = "v" . int(rand(1000)) . $i;
+ my $r = $cli->execute_insert(1, [ $k, $v ]);
+ if ($r->[0] != 0) {
+ die;
+ }
+ print "$i/$tablesize\n" if $i % 1000 == 0;
+ }
+ } elsif ($action eq "hsread") {
+ print("HSREAD $db.$table op=$op loop=$loop\n");
+ my $moreflds = get_select_moreflds_str();
+ $cli->open_index(1, $db, $table, '', "k,v$moreflds");
+ for (my $i = 0; $i < $loop; ++$i) {
+ my $k = "" . int(rand($tablesize));
+ # print "k=$k\n";
+ my $r = $cli->execute_find(1, $op, [ $k ], 1, 0);
+ if ($verbose >= 10) {
+ my $len = scalar(@{$r});
+ print "LEN=$len";
+ for my $e (@{$r}) {
+ print " [$e]";
+ }
+ print "\n";
+ }
+ print "$i/$loop\n" if $i % 1000 == 0;
+ }
+ } elsif ($action eq "hsupdate") {
+ my $vbase = "v" . int(rand(1000));
+ print("HSUPDATE $db.$table op=$op loop=$loop vbase=$vbase\n");
+ $cli->open_index(1, $db, $table, '', 'v');
+ for (my $i = 0; $i < $loop; ++$i) {
+ my $k = "" . int(rand($tablesize));
+ my $v = $vbase . $i;
+ print "k=$k v=$v\n";
+ my $r = $cli->execute_update(1, $op, [ $k ], 1, 0,
+ [ $v ]);
+ if ($verbose >= 10) {
+ print "UP k=$k v=$v\n";
+ }
+ print "$i/$loop\n" if $i % 1000 == 0;
+ }
+ } elsif ($action eq "hsdelete") {
+ print("HSDELETE $db.$table op=$op loop=$loop\n");
+ $cli->open_index(1, $db, $table, '', '');
+ for (my $i = 0; $i < $loop; ++$i) {
+ my $k = "" . int(rand($tablesize));
+ print "k=$k\n";
+ my $r = $cli->execute_delete(1, $op, [ $k ], 1, 0);
+ if ($verbose >= 10) {
+ print "DEL k=$k\n";
+ }
+ print "$i/$loop\n" if $i % 1000 == 0;
+ }
+ } elsif ($action eq "verify") {
+ verify_do();
+ }
+}
+
+sub verify_do {
+ my ($fail_cnt, $ok_cnt) = (0, 0);
+ my $sth = $dbh->prepare("select v from $db.$table where k = ?");
+ use FileHandle;
+ my $fh = new FileHandle($file, "r");
+ while (my $line = <$fh>) {
+ chomp($line);
+ my @vec = split(/\t/, $line);
+ my $k = $vec[3];
+ my $v = $vec[7];
+ next if (!defined($k) || !defined($v));
+ # print "$k $v\n";
+ $sth->execute($k);
+ my $aref = $sth->fetchrow_arrayref();
+ if (!defined($aref)) {
+ print "FAILED: $k notfound\n";
+ ++$fail_cnt;
+ } else {
+ my $gv = $aref->[0];
+ if ($gv ne $v) {
+ print "FAILED: $k got=$gv expected=$v\n";
+ ++$fail_cnt;
+ } else {
+ print "OK: $k $v $gv\n" if $verbose >= 10;
+ ++$ok_cnt;
+ }
+ }
+ }
+ print "OK=$ok_cnt FAIL=$fail_cnt\n";
+}
+
+sub get_conf {
+ my ($key, $def) = @_;
+ my $val = $conf{$key};
+ if ($val) {
+ print "$key=$val\n";
+ } else {
+ $val = $def;
+ $def ||= '';
+ print "$key=$def(default)\n";
+ }
+ return $val;
+}
+
+sub get_createtbl_moreflds_str {
+ my $s = "";
+ for (my $j = 0; $j < $num_moreflds; ++$j) {
+ $s .= ",$moreflds_prefix$j varchar(30)";
+ }
+ return $s;
+}
+
+sub get_select_moreflds_str {
+ my $s = "";
+ for (my $i = 0; $i < $num_moreflds; ++$i) {
+ $s .= ",$moreflds_prefix$i";
+ }
+ return $s;
+}
+
+sub get_insert_moreflds_str {
+ my $s = "";
+ for (my $i = 0; $i < $num_moreflds; ++$i) {
+ $s .= ",?";
+ }
+ return $s;
+}
+
diff --git a/plugin/handler_socket/client/hstest_hs.sh b/plugin/handler_socket/client/hstest_hs.sh
new file mode 100755
index 00000000000..1b9eee188ec
--- /dev/null
+++ b/plugin/handler_socket/client/hstest_hs.sh
@@ -0,0 +1,4 @@
+#!/bin/bash
+
+exec ./hstest test=10 tablesize=10000 host=localhost hsport=9998 num=10000000 \
+ num_threads=100 timelimit=10 $@
diff --git a/plugin/handler_socket/client/hstest_hs_more50.sh b/plugin/handler_socket/client/hstest_hs_more50.sh
new file mode 100755
index 00000000000..b7539c52921
--- /dev/null
+++ b/plugin/handler_socket/client/hstest_hs_more50.sh
@@ -0,0 +1,4 @@
+#!/bin/bash
+
+exec ./hstest test=10 key_mask=9999 host=localhost port=9998 num=10000000 \
+ num_threads=100 timelimit=10 moreflds=50 $@
diff --git a/plugin/handler_socket/client/hstest_md.sh b/plugin/handler_socket/client/hstest_md.sh
new file mode 100755
index 00000000000..8129f884d24
--- /dev/null
+++ b/plugin/handler_socket/client/hstest_md.sh
@@ -0,0 +1,7 @@
+#!/bin/bash
+
+./hstest test=7 key_mask=9999 host=localhost port=11211 num=10000 \
+ num_threads=10 timelimit=10 op=R $@
+./hstest test=7 key_mask=9999 host=localhost port=11211 num=1000000 \
+ num_threads=100 timelimit=10 op=G $@
+
diff --git a/plugin/handler_socket/client/hstest_my.sh b/plugin/handler_socket/client/hstest_my.sh
new file mode 100755
index 00000000000..cf917cf48b8
--- /dev/null
+++ b/plugin/handler_socket/client/hstest_my.sh
@@ -0,0 +1,3 @@
+#!/bin/bash
+exec ./hstest test=9 tablesize=9999 host=localhost mysqlport=3306 num=1000000 \
+ num_threads=100 verbose=1 timelimit=10 $@
diff --git a/plugin/handler_socket/client/hstest_my_more50.sh b/plugin/handler_socket/client/hstest_my_more50.sh
new file mode 100755
index 00000000000..6782b5e8ed6
--- /dev/null
+++ b/plugin/handler_socket/client/hstest_my_more50.sh
@@ -0,0 +1,3 @@
+#!/bin/bash
+exec ./hstest test=9 key_mask=9999 host=localhost port=3306 num=1000000 \
+ num_threads=100 verbose=1 timelimit=10 moreflds=50 $@
diff --git a/plugin/handler_socket/configure.ac b/plugin/handler_socket/configure.ac
new file mode 100644
index 00000000000..36e85d24cab
--- /dev/null
+++ b/plugin/handler_socket/configure.ac
@@ -0,0 +1,134 @@
+# -*- Autoconf -*-
+# Process this file with autoconf to produce a configure script.
+
+#AC_PREREQ([2.63b])
+AC_INIT([handlersocket-plugin], [1.0.6], [https://github.com/ahiguti/HandlerSocket-Plugin-for-MySQL/issues])
+AC_CONFIG_HEADERS([config.h])
+AM_INIT_AUTOMAKE([-Wall -Werror foreign])
+AC_CONFIG_SRCDIR([libhsclient/fatal.cpp])
+AC_CONFIG_MACRO_DIR([m4])
+
+AC_PROG_CC
+AC_PROG_CXX
+AC_PROG_CPP
+AC_PROG_LIBTOOL
+
+AC_DEFUN([CONFIG_OPTION_MYSQL],[
+ AC_MSG_CHECKING([mysql source])
+
+ MYSQL_SOURCE_VERSION=
+ MYSQL_INC=
+ ac_mysql_source_dir=
+ AC_ARG_WITH([mysql-source],
+ [AS_HELP_STRING([--with-mysql-source=PATH], [MySQL source directory PATH])],
+ [
+ ac_mysql_source_dir=`cd $withval && pwd`
+ if test -f "$ac_mysql_source_dir/sql/handler.h" ; then
+ MYSQL_INC="-I$ac_mysql_source_dir/sql"
+ MYSQL_INC="$MYSQL_INC -I$ac_mysql_source_dir/include"
+ MYSQL_INC="$MYSQL_INC -I$ac_mysql_source_dir/regex"
+ MYSQL_INC="$MYSQL_INC -I$ac_mysql_source_dir"
+ AC_SUBST(MYSQL_INC)
+ if test -f "$ac_mysql_source_dir/VERSION"; then
+ source "$ac_mysql_source_dir/VERSION"
+ MYSQL_SOURCE_VERSION="$MYSQL_VERSION_MAJOR.$MYSQL_VERSION_MINOR.$MYSQL_VERSION_PATCH"
+ else
+ if test -f "$ac_mysql_source_dir/configure.in"; then
+ MYSQL_SOURCE_VERSION=`cat $ac_mysql_source_dir/configure.in | grep "\[[MySQL Server\]]" | sed -e "s|.*\([[0-9]]\+\.[[0-9]]\+\.[[0-9]]\+[[0-9a-zA-Z\_\-]]*\).*|\1|"`
+ else
+ AC_MSG_ERROR([invalid MySQL source directory: $ac_mysql_source_dir])
+ fi
+ fi
+ AC_MSG_RESULT([yes: Using $ac_mysql_source_dir, version $MYSQL_SOURCE_VERSION])
+ else
+ AC_MSG_ERROR([invalid MySQL source directory: $ac_mysql_source_dir])
+ fi
+ ],
+ [AC_MSG_ERROR([--with-mysql-source=PATH is required for standalone build])]
+ )
+
+ MYSQL_BIN_VERSION=
+ ac_mysql_config=
+ AC_ARG_WITH([mysql-bindir],
+ [AS_HELP_STRING([--with-mysql-bindir=PATH], [MySQL binary directory PATH. This should be the directory where mysql_config is located.])],
+ [
+ mysql_bin_dir=`cd $withval 2> /dev/null && pwd || echo ""`
+ ac_mysql_config="$mysql_bin_dir/mysql_config"
+ ],
+ [
+ AC_PATH_PROG([ac_mysql_config], [mysql_config])
+ ]
+ )
+
+ AC_MSG_CHECKING([mysql binary])
+ if test ! -x "$ac_mysql_config" ; then
+ AC_MSG_ERROR([mysql_config not found! You have to specify the directory where mysql_config resides to --with-mysql-bindir=PATH.])
+ fi
+
+ MYSQL_CFLAGS_ADD=`"$ac_mysql_config" --cflags`
+ MYSQL_CFLAGS="$MYSQL_CFLAGS $MYSQL_CFLAGS_ADD -DFORCE_DBUG_OFF"
+ # FIXME
+ AC_SUBST(MYSQL_CFLAGS)
+
+ MYSQL_BIN_VERSION=`"$ac_mysql_config" --version`
+ AC_MSG_RESULT([yes: Using $ac_mysql_config, version $MYSQL_BIN_VERSION])
+
+ MYSQL_LIB=`"$ac_mysql_config" --libs_r`
+ LIB_DIR=`echo $MYSQL_LIB | sed -e "s|.*-L/|/|" | sed -e "s| .*||"`
+ # FIXME
+ if test a`basename "$LIB_DIR"` = amysql ; then
+ MYSQL_LIB="-L`dirname $LIB_DIR` $MYSQL_LIB"
+ # FIXME
+ fi
+ AC_SUBST(MYSQL_LIB)
+
+ if test a$MYSQL_SOURCE_VERSION != a$MYSQL_BIN_VERSION ; then
+ AC_MSG_ERROR([MySQL source version does not match MySQL binary version])
+ fi
+
+ AC_MSG_CHECKING([mysql plugin dir])
+ ac_mysql_plugin_dir=
+ AC_ARG_WITH([mysql-plugindir],
+ [AS_HELP_STRING([--with-mysql-plugindir=PATH], [MySQL plugin directory where handlersocket.so to be copied])],
+ [
+ ac_mysql_plugin_dir=`cd $withval && pwd`
+ if test -d "$ac_mysql_plugin_dir/" ; then
+ PLUGIN_DIR="$ac_mysql_plugin_dir"
+ AC_SUBST(PLUGIN_DIR)
+ AC_MSG_RESULT([yes: Using $ac_mysql_plugin_dir])
+ else
+ AC_MSG_ERROR([invalid MySQL plugin directory : $ac_mysql_plugin_dir])
+ fi
+ ],
+ [
+ LIB_DIR_TMP=`"$ac_mysql_config" --plugindir`
+ if test ! -d "$LIB_DIR_TMP"; then
+ LIB_DIR_TMP=`"$ac_mysql_config" --libs_r | sed -e "s|.*-L/|/|" | sed -e "s| .*||"`/plugin
+ # FIXME
+ fi
+ ac_mysql_plugin_dir=$LIB_DIR_TMP
+ PLUGIN_DIR="$ac_mysql_plugin_dir"
+ AC_SUBST(PLUGIN_DIR)
+ AC_MSG_RESULT([--with-mysql-plugindir was not set. Using $ac_mysql_plugin_dir])
+ ]
+ )
+])
+
+HANDLERSOCKET_SUBDIRS="libhsclient"
+AC_ARG_ENABLE(handlersocket_server,
+ [ --enable-handlersocket-server build HandlerSocket plugin (defalut=yes)])
+if test "$enable_handlersocket_server" != "no"; then
+ CONFIG_OPTION_MYSQL
+ HANDLERSOCKET_SUBDIRS="libhsclient handlersocket client"
+fi
+AC_SUBST(HANDLERSOCKET_SUBDIRS)
+
+CFLAGS="$CFLAGS -Werror"
+CXXFLAGS="$CXXFLAGS -Wall -g -fno-rtti -fno-exceptions -fPIC -DPIC"
+
+AC_CONFIG_FILES([Makefile
+ handlersocket/Makefile
+ libhsclient/Makefile
+ client/Makefile])
+
+AC_OUTPUT
diff --git a/plugin/handler_socket/docs-en/about-handlersocket.en.txt b/plugin/handler_socket/docs-en/about-handlersocket.en.txt
new file mode 100644
index 00000000000..0a13a2713d6
--- /dev/null
+++ b/plugin/handler_socket/docs-en/about-handlersocket.en.txt
@@ -0,0 +1,72 @@
+
+-----------------------------------------------------------------------------
+HandlerSocket plugin for MySQL
+
+Copyright (c) 2010 DeNA Co.,Ltd.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of DeNA Co.,Ltd. nor the names of its contributors
+ may be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY DeNA Co.,Ltd. "AS IS" AND ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+EVENT SHALL DeNA Co.,Ltd. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+-----------------------------------------------------------------------------
+About HandlerSocket
+
+HandlerSocket is a NoSQL plugin for MySQL. It works as a daemon inside the
+mysqld process, accept tcp connections, and execute requests from clients.
+HandlerSocket does not support SQL queries. Instead, it supports simple CRUD
+operations on tables.
+
+Because of the following reasons, HandlerSocket is much faster than the
+mysqld/libmysql pair in some circumstances:
+
+ - HandlerSocket manipulates data without parsing SQL, which causes less
+ CPU usage.
+ - HandlerSocket reads many requests from clients and executes their
+ requests in bulk, which causes less CPU and disk usage.
+ - HandlerSocket client/server protocol is more compact than the
+ mysql/libmysql pair, which causes less network usage.
+
+The current version of HandlerSocket only works with GNU/Linux. The source
+archive of HandlerSocket includes a C++ and a Perl client libraries.
+Here is a list of other language bindings:
+
+ - PHP
+ http://openpear.org/package/Net_HandlerSocket
+ http://github.com/tz-lom/HSPHP
+ http://code.google.com/p/php-handlersocket/
+ - Java
+ http://code.google.com/p/handlersocketforjava/
+ - Python
+ https://code.launchpad.net/~songofacandy/+junk/pyhandlersocket
+ - Ruby
+ https://github.com/winebarrel/ruby-handlersocket
+ https://github.com/miyucy/handlersocket
+ - JavaScript(Node.js)
+ https://github.com/koichik/node-handlersocket
+
+The home of HandlerSocket is here:
+ https://github.com/ahiguti/HandlerSocket-Plugin-for-MySQL
+
+More documents are available in docs-en/ and docs-ja/ directories.
+
diff --git a/plugin/handler_socket/docs-en/configuration-options.en.txt b/plugin/handler_socket/docs-en/configuration-options.en.txt
new file mode 100644
index 00000000000..f0c0be32f07
--- /dev/null
+++ b/plugin/handler_socket/docs-en/configuration-options.en.txt
@@ -0,0 +1,87 @@
+
+-----------------------------------------------------------------
+handlersocket_verbose (default = 10, min = 0, max = 10000)
+
+ Specify the logging verboseness.
+
+-----------------------------------------------------------------
+handlersocket_address (default = '')
+
+ Specify the address to bind. If empty, it binds to 0.0.0.0.
+
+-----------------------------------------------------------------
+handlersocket_port (default = '9998')
+
+ Specify the port to bind. This option is for the listener for
+ read requests. If empty, the listener is disabled.
+
+-----------------------------------------------------------------
+handlersocket_port_wr (default = '9999')
+
+ Specify the port to bind. This option is for the listener for
+ write requests. If empty, the listener is disabled.
+
+-----------------------------------------------------------------
+handlersocket_epoll (default = 1, min = 0, max = 1)
+
+ Specify whether handlersocket uses epoll for I/O multiplexing.
+
+-----------------------------------------------------------------
+handlersocket_threads (default = 16, min = 1, max = 3000)
+
+ Specify the number of handlersocket worker threads. This option
+ is for the listener for read requests. Recommended value is
+ (the number of CPU cores * 2).
+
+-----------------------------------------------------------------
+handlersocket_threads_wr (default = 1, min = 1, max = 3000)
+
+ Specify the number of handlersocket worker threads. This option
+ is for the listener for write requests. Recommended value is 1.
+
+-----------------------------------------------------------------
+handlersocket_timeout (default = 300, min = 30, max = 3600)
+
+ Specify the socket timeout in seconds.
+
+-----------------------------------------------------------------
+handlersocket_backlog (default = 32768, min = 5, max = 1000000)
+
+ Specify the length of the listen backlog.
+
+-----------------------------------------------------------------
+handlersocket_sndbuf (default = 0, min = 0, max = 1677216)
+
+ Specify the maximum socket send buffer in bytes. If 0, the
+ system-wide default value is set.
+
+-----------------------------------------------------------------
+handlersocket_rcvbuf (default = 0, min = 0, max = 1677216)
+
+ Specify the maximum socket receive buffer in bytes. If 0, the
+ system-wide default value is set.
+
+-----------------------------------------------------------------
+handlersocket_readsize (default = 0, min = 0, max = 1677216)
+
+ Specify the minimum length of the handlersocket request buffer.
+ Larger value can make handlersocket faster for large requests,
+ but can consume memory. The default value is possibly 4096.
+
+-----------------------------------------------------------------
+handlersocket_accept_balance (default = 0, min = 0, max = 10000)
+
+ When this option is set to non-zero, handlersocket tries to
+ balance accepted connections among threads. Non-zero is
+ recommended if you use persistent connections (i.e., connection
+ pooling on the client side).
+
+-----------------------------------------------------------------
+handlersocket_wrlock_timeout (default = 12, min = 0, max = 3600)
+
+ Specify the lock timeout in seconds. When a write request is
+ performed, handlersocket locks an advisory lock named
+ 'handlersocket_wr'. This option sets the timeout for the
+ locking.
+
+
diff --git a/plugin/handler_socket/docs-en/installation.en.txt b/plugin/handler_socket/docs-en/installation.en.txt
new file mode 100644
index 00000000000..8e680ed35f1
--- /dev/null
+++ b/plugin/handler_socket/docs-en/installation.en.txt
@@ -0,0 +1,91 @@
+1. Building Handlersocket
+
+ Handlersocket mainly consists of libhsclient, handlersocket, and C++/Perl clients. libhsclient is a common library shared from both client and server(plugin). handlersocket is a MySQL daemon plugin.
+ To build Handlersocket, you need both MySQL source code and MySQL binary. It is not required to pre-build MySQL source code, but source itself is needed because Handlersocket depends on MySQL header files that only MySQL source distribution contains. MySQL binary is just a normal MySQL binary distribution. You can use official MySQL binaries provided by Oracle.
+ Since Handlersocket uses daemon plugin interface supported from MySQL 5.1,
+MySQL 5.1 or higher version is required.
+ Please make sure that you use identical MySQL version between MySQL source
+and MySQL binary. Otherwise you might encounter serious problems (i.e. server
+crash, etc).
+ Here are steps to build Handlersocket.
+
+* Get MySQL source code
+
+* Get MySQL binary
+
+* Build Handlersocket
+ $ ./autogen.sh
+ $ ./configure --with-mysql-source=/work/mysql-5.1.50 --with-mysql-bindir=/work/mysql-5.1.50-linux-x86_64-glibc23/bin --with-mysql-plugindir=/work/mysql-5.1.50-linux-x86_64-glibc23/lib/plugin
+
+ --with-mysql-source refers to the top of MySQL source directory,
+--with-mysql-bindir refers to where MySQL binary executables (i.e.
+mysql_config) are located, and --with-mysql-plugindir refers to a plugin
+directory where plugin libraries (*.so) are installed.
+
+ $ make
+ $ sudo make install
+
+ Both libhsclient and the handlersocket plugin will be installed.
+
+
+2. Using Handlersocket
+
+Append configuration options for handlersocket to my.cnf.
+
+ [mysqld]
+ loose_handlersocket_port = 9998
+ # the port number to bind to (for read requests)
+ loose_handlersocket_port_wr = 9999
+ # the port number to bind to (for write requests)
+ loose_handlersocket_threads = 16
+ # the number of worker threads (for read requests)
+ loose_handlersocket_threads_wr = 1
+ # the number of worker threads (for write requests)
+ open_files_limit = 65535
+ # to allow handlersocket accept many concurrent
+ # connections, make open_files_limit as large as
+ # possible.
+
+Log in to mysql as root, and execute the following query.
+
+ mysql> install plugin handlersocket soname 'handlersocket.so';
+
+If handlersocket.so is successfully installed, it starts
+accepting connections on port 9998 and 9999. Running
+'show processlist' should show handlersocket worker threads.
+
+-----------------------------------------------------------------
+On the client side, you need to install libhsclient for c++ apps
+and perl-Net-HandlerSocket for perl apps. They do not require
+MySQL to compile.
+
+ $ ./autogen.sh
+ $ ./configure --disable-handlersocket-server
+ $ make
+ $ sudo make install
+ $ cd perl-Net-HandlerSocket
+ $ perl Makefile.PL
+ $ make
+ $ sudo make install
+
+-----------------------------------------------------------------
+Alternatively, you can use the rpm installation. If your OS
+supports rpms, you can use the following commands to build and
+install handlersocket rpm packages.
+
+(Server side, installs HandlerSocket plugin)
+ $ ./autogen.sh
+ $ ./configure --with-mysql-source=/work/mysql-5.1.50 --with-mysql-bindir=/work/mysql-5.1.50-linux-x86_64-glibc23/bin --with-mysql-plugindir=/work/mysql-5.1.50-linux-x86_64-glibc23/lib/plugin
+ $ make rpm_cli
+ $ sudo rpm -U dist/RPMS/*/libhsclient*.rpm
+ $ make rpm_c
+ $ sudo rpm -U dist/RPMS/*/handlersocket*.rpm
+
+(Client side, installs client libraries)
+ $ ./autogen.sh
+ $ ./configure --disable-handlersocket-server
+ $ make rpm_cli
+ $ sudo rpm -U dist/RPMS/*/libhsclient*.rpm
+ $ make rpm_perl
+ $ sudo rpm -U dist/RPMS/*/perl-Net-HandlerSocket*.rpm
+
diff --git a/plugin/handler_socket/docs-en/perl-client.en.txt b/plugin/handler_socket/docs-en/perl-client.en.txt
new file mode 100644
index 00000000000..2b863c638f0
--- /dev/null
+++ b/plugin/handler_socket/docs-en/perl-client.en.txt
@@ -0,0 +1,126 @@
+
+-----------------------------------------------------------------
+To open a connection to the handlersocket plugin, you need to
+create a Net::HandlerSocket object.
+
+ use Net::HandlerSocket;
+ my $args = { host => 'localhost', port => 9998 };
+ my $hs = new Net::HandlerSocket($args);
+
+-----------------------------------------------------------------
+Before executing table operations, you need to open an index to
+work with.
+
+ my $err = $hs->open_index(3, 'database1', 'table1', 'PRIMARY',
+ 'f1,f2');
+ die $hs->get_error() if $res->[0] != 0;
+
+The first argument for open_index is an integer value which is
+used to identify an open table, which is only valid within the
+same Net::HandlerSocket object. The 4th argument is the name of
+index to open. If 'PRIMARY' is specified, the primary index is
+open. The 5th argument is a comma-separated list of column names.
+
+-----------------------------------------------------------------
+To read a record from a table using an index, call the
+execute_single method.
+
+ my $res = $hs->execute_single(3, '=', [ 'foo' ], 1, 0);
+ die $hs->get_error() if $res->[0] != 0;
+ shift(@$res);
+
+The first argument must be an integer which has specified as the
+first argument for open_index on the same Net::HandlerSocket
+object. The second argument specifies the search operation. The
+current version of handlersocket supports '=', '>=', '<=', '>',
+and '<'. The 3rd argument specifies the key to find, which must
+an arrayref whose length is equal to or smaller than the number
+of key columns of the index. The 4th and the 5th arguments
+specify the maximum number of records to be retrieved, and the
+number of records skipped before retrieving records. The columns
+to be retrieved are specified by the 5th argument for the
+corresponding open_index call.
+
+The execute_single method always returns an arrayref. The first
+element is the error code, which is 0 when no error is occured.
+The remaining are the field values. If more than one record is
+returned, it is flatten to an 1-dimensional array. For example,
+when 5 records that have 3 columns are returned, you can retrieve
+values using the following code.
+
+ die $hs->get_error() if $res->[0] != 0;
+ shift(@$res);
+ for (my $row = 0; $row < 5; ++$row) {
+ for (my $col = 0; $col < 3; ++$col) {
+ my $value = $res->[$row * 5 + $col];
+ # ...
+ }
+ }
+
+-----------------------------------------------------------------
+To update or delete records, you need to specify more arguments
+for the execute_single method. Note that the Net::HandlerSocket
+object must be connected to a handlersocket worker for write
+operations, which is port 9999 by default.
+(For safety, the port 9998 only allows read operations, and the
+port 9999 allows write operations also. The port 9999 allows
+read operations too, but slower than 9998 because of record
+locking etc.. Port numbers can be changed using the
+'handlersocket_port' and the 'handlersocket_port_wr'
+configuration options of mysqld.)
+
+ my $args = { host => 'localhost', port => 9999 };
+ my $hs = new Net::HandlerSocket($args);
+
+ my $res = $hs->execute_single(3, '=', [ 'bar' ], 1, 0, 'U',
+ [ 'fubar', 'hoge' ]);
+ die $hs->get_error() if $res->[0] != 0;
+ my $num_updated_rows = $res->[1];
+
+ my $res = $hs->execute_single(3, '=', [ 'baz' ], 1, 0, 'D');
+ die $hs->get_error() if $res->[0] != 0;
+ my $num_deleted_rows = $res->[1];
+
+The 6th argument for execute_single specifies the modification
+operation. The current version supports 'U' and 'D'. For the 'U'
+operation, the 7th argument specifies the new value for the row.
+The columns to be modified are specified by the 5th argument for
+the corresponding open_index call. For the 'D' operation, the
+7th argument can be omitted.
+
+-----------------------------------------------------------------
+The execute_single method can be used for inserting records also.
+
+ my $res = $hs->execute_single(3, '+', [ 'foo', 'bar', 'baz' ]);
+ die $hs->get_error() if $res->[0] != 0;
+ my $num_inserted_rows = $res->[1];
+
+The 3rd argument must be an arrayref whose elements correspond to
+the 5th argument for the corresponding open_index call. If there
+is a column which is not appeared in the 5th argument for the
+open_index, the default value for the column is set.
+
+-----------------------------------------------------------------
+Multiple operations can be executed in a single call. Executing
+multiple operations in a single call is much faster than
+executing them separatedly.
+
+ my $rarr = $hs->execute_multi([
+ [ 0, '>=', [ 'foo' ], 5, 0 ],
+ [ 2, '=', [ 'bar' ], 1, 0 ],
+ [ 4, '<', [ 'baz' ], 10, 5 ],
+ ]);
+ for my $res (@$rarr) {
+ die $hs->get_error() if $res->[0] != 0;
+ shift(@$res);
+ # ...
+ }
+
+-----------------------------------------------------------------
+When an error is occured, the first element of the returned
+arrayref becomes a non-zero value. A negative value indicates
+that an I/O error is occured and the Net::HandlerSocket object
+should be disposed. A positive value means that the connection is
+still active and the Net::HandlerSocket object can be reused
+later.
+
diff --git a/plugin/handler_socket/docs-en/protocol.en.txt b/plugin/handler_socket/docs-en/protocol.en.txt
new file mode 100644
index 00000000000..ee00ab352d8
--- /dev/null
+++ b/plugin/handler_socket/docs-en/protocol.en.txt
@@ -0,0 +1,148 @@
+
+----------------------------------------------------------------------------
+The HandlerSocket protocol
+
+----------------------------------------------------------------------------
+Basic syntax
+
+- The HandlerSocket protocol is line-based. Each line ends with LF(0x0a).
+- Each line consists a concatenation of tokens separated by HT(0x09).
+- A token is either NULL or an encoded string. Note that you need to
+ distinguish NULL from an empty string, as most DBMs does so.
+- NULL is expressed as a single NUL(0x00).
+- An encoded string is a string with the following encoding rules.
+ - Characters in the range [0x10 - 0xff] are not encoded.
+ - A character in the range [0x00 - 0x0f] is prefixed by 0x01 and
+ shifted by 0x40. For example, 0x03 is encoded as 0x01 0x43.
+- Note that a string can be empty. A continuation of 0x09 0x09 means that
+ there is an empty string between them. A continuation of 0x09 0x0a means
+ that there is an empty string at the end of the line.
+
+----------------------------------------------------------------------------
+Request and Response
+
+- The HandlerSocket protocol is a simple request/response protocol. After a
+ connection is established, the client side sends a request, and then the
+ server side sends a response.
+- A request/response consists of a single line.
+- Requests can be pipelined; That is, you can send multiple requests (ie.
+ lines) at one time, and receive responses for them at one time.
+
+----------------------------------------------------------------------------
+'open_index' request
+
+The 'open_index' request has the following syntax.
+
+ P <indexid> <dbname> <tablename> <indexname> <columns>
+
+- <indexid> is a number in decimal.
+- <dbname>, <tablename>, and <indexname> are strings. To open the primary
+ key, use PRIMARY as <indexname>.
+- <columns> is a comma-separated list of column names.
+
+Once an 'open_index' request is issued, the HandlerSocket plugin opens the
+specified index and keep it open until the client connection is closed. Each
+open index is identified by <indexid>. If <indexid> is already open, the old
+open index is closed. You can open the same combination of <dbname>
+<tablename> <indexname> multple times, possibly with different <columns>.
+For efficiency, keep <indexid> small as far as possible.
+
+----------------------------------------------------------------------------
+Getting data
+
+The 'find' request has the following syntax.
+
+ <indexid> <op> <vlen> <v1> ... <vn> <limit> <offset>
+
+- <indexid> is a number. This number must be an <indexid> specified by a
+ 'open_index' request executed previously on the same connection.
+- <op> specifies the comparison operation to use. The current version of
+ HandlerSocket supports '=', '>', '>=', '<', and '<='.
+- <vlen> indicates the length of the trailing parameters <v1> ... <vn>. This
+ must be smaller than or equal to the number of index columns specified by
+ specified by the corresponding 'open_index' request.
+- <v1> ... <vn> specify the index column values to fetch.
+- <limit> and <offset> are numbers. These parameters can be omitted. When
+ omitted, it works as if 1 and 0 are specified.
+
+----------------------------------------------------------------------------
+Updating/Deleting data
+
+The 'find_modify' request has the following syntax.
+
+ <indexid> <op> <vlen> <v1> ... <vn> <limit> <offset> <mop> <m1> ... <mk>
+
+- <mop> is either 'U' (update) or 'D' (delete).
+- <m1> ... <mk> specifies the column values to set. The length of <m1> ...
+ <mk> must be smaller than or equal to the length of <columns> specified by
+ the corresponding 'open_index' request. If <mop> is 'D', these parameters
+ are ignored.
+
+----------------------------------------------------------------------------
+Inserting data
+
+The 'insert' request has the following syntax.
+
+ <indexid> '+' <vlen> <v1> ... <vn>
+
+- <vlen> indicates the length of the trailing parameters <v1> ... <vn>. This
+ must be smaller than or equal to the length of <columns> specified by the
+ corresponding 'open_index' request.
+- <v1> ... <vn> specify the column values to set. For columns not in
+ <columns>, the default values for each column are set.
+
+----------------------------------------------------------------------------
+Response syntax
+
+HandlerSocket returns a response of the following syntax for each request.
+
+ <errorcode> <numcolumns> <r1> ... <rn>
+
+- <errorcode> indicates whether the request has successfully executed or not.
+ '0' means success. Non-zero means an error.
+- <numcolumns> indicates the number of columns of the result set.
+- <r1> ... <rn> is the result set. The length of <r1> ... <rn> is always a
+ multiple of <numcolumns>. It is possible that <r1> ... <rn> is empty.
+
+If <errorcode> is non-zero, <numcolumns> is always 1 and <r1> indicates a
+human-readable error message, though sometimes <r1> is not provided.
+
+----------------------------------------------------------------------------
+Response for 'open_index'
+
+If 'open_index' is succeeded, HandlerSocket returns a line of the following
+syntax.
+
+ 0 1
+
+----------------------------------------------------------------------------
+Response for 'find'
+
+If 'find' is succeeded, HandlerSocket returns a line of the following
+syntax.
+
+ 0 <numcolumns> <r1> ... <rn>
+
+- <numcolumns> always equals to the length of <columns> of the corresponding
+ 'open_index' request.
+- <r1> ... <rn> is the result set. If N rows are found, the length of <r1>
+ ... <rn> becomes ( <numcolumns> * N ).
+
+----------------------------------------------------------------------------
+Response for 'find_modify'
+
+If 'find_modify' is succeeded, HandlerSocket returns a line of the following
+syntax.
+
+ 0 1 <nummod>
+
+- <nummod> is the number of modified rows.
+
+----------------------------------------------------------------------------
+Response for 'insert'
+
+If 'insert' is succeeded, HanderSocket returns a line of the following
+syntax.
+
+ 0 1
+
diff --git a/plugin/handler_socket/docs-ja/about-handlersocket.ja.txt b/plugin/handler_socket/docs-ja/about-handlersocket.ja.txt
new file mode 100644
index 00000000000..2a152e87545
--- /dev/null
+++ b/plugin/handler_socket/docs-ja/about-handlersocket.ja.txt
@@ -0,0 +1,51 @@
+
+
+-----------------------------------------------------------------
+ソースコードã®åˆ©ç”¨ã«ã‚ãŸã£ã¦ã®å…責事項
+
+本ソフトウェアã®é–‹ç™ºè€…ãŠã‚ˆã³æ ªå¼ä¼šç¤¾ãƒ‡ã‚£ãƒ¼ãƒ»ã‚¨ãƒŒãƒ»ã‚¨ãƒ¼ã¯ã€æœ¬ãƒ•ãƒˆ
+ウェアã®ä¸ç¨¼å‹•ã€ç¨¼å‹•ä¸è‰¯ã‚’å«ã‚€æ³•å¾‹ä¸Šã®ç‘•ç–µæ‹…ä¿è²¬ä»»ã€ãã®ä»–ä¿è¨¼è²¬
+任を負ã‚ãªã„ã‚‚ã®ã¨ã—ã¾ã™ã€‚ã¾ãŸã€æœ¬ã‚½ãƒ•ãƒˆã‚¦ã‚¨ã‚¢ã®é–‹ç™ºè€…ãŠã‚ˆã³æ ªå¼
+会社ディー・エヌ・エーã¯ã€æœ¬ã‚½ãƒ•ãƒˆã‚¦ã‚§ã‚¢ã®å•†å“性ã€ã¾ãŸã¯ãŠå®¢æ§˜ã®
+特定ã®ç›®çš„ã«å¯¾ã™ã‚‹é©åˆæ€§ã«ã¤ã„ã¦ã€ã„ã‹ãªã‚‹ä¿è¨¼ã‚‚è² ã‚ãªã„ã‚‚ã®ã¨ã—
+ã¾ã™ã€‚
+
+-----------------------------------------------------------------
+handlersocket pluginã«ã¤ã„ã¦
+
+mysqlサーãƒã«å¸¸é§ã—ã€innodbç­‰ã®ã‚¹ãƒˆãƒ¬ãƒ¼ã‚¸ã‚¨ãƒ³ã‚¸ãƒ³ã¸ã®ç›´æŽ¥ã®ã‚¢ã‚¯ã‚»
+スをæä¾›ã™ã‚‹ãƒ—ラグインã§ã™ã€‚handlersocketプラグインã¯è‡ªå‰ã®ãƒªã‚¹
+ナーをæŒã¡ã€å°‚用ã®ã‚¯ãƒ©ã‚¤ã‚¢ãƒ³ãƒˆãƒ©ã‚¤ãƒ–ラリ(libhsclient)を使ã£ã¦ãã‚Œ
+ã«ã‚¢ã‚¯ã‚»ã‚¹ã—ã¾ã™ã€‚
+
+mysqlã®æ¨™æº–クライアントライブラリ(libmysql)を使ã£ãŸã‚¢ã‚¯ã‚»ã‚¹ã¨æ¯”ã¹
+ã¦ã€ä»¥ä¸‹ã®ã‚ˆã†ãªåˆ©ç‚¹ãŒã‚ã‚Šã¾ã™ã€‚
+・接続ã‚ãŸã‚Šã«æ¶ˆè²»ã™ã‚‹ãƒªã‚½ãƒ¼ã‚¹ãŒå°‘ãªã„ãŸã‚ã€åŒæ™‚接続数ãŒäº‹å®Ÿä¸Šç„¡
+ 制é™ã€‚ã—ãŸãŒã£ã¦æŽ¥ç¶šæ•°ã‚’æ°—ã«ã›ãšæŒç¶šæŽ¥ç¶šã‚’使ãˆã¾ã™ã€‚
+・高速(å˜ç´”ãªå‚照クエリã§3å€ã€œ10å€ç¨‹åº¦)。
+・通信プロトコルãŒã‚³ãƒ³ãƒ‘クト。libmysqlを使ã†ã¨ãƒ‡ãƒ¼ã‚¿è»¢é€æ™‚ã«ãƒ¬
+ コードåãªã©ãŒä»˜éšã™ã‚‹ãŸã‚ã«é€šä¿¡å†…容ãŒå†—é•·ã§ã™ãŒã€libhsclientã§
+ ã¯ãƒ‡ãƒ¼ã‚¿ã®ã¿ãŒè»¢é€ã•ã‚Œã‚‹ãŸã‚ã€å¸¯åŸŸæ¶ˆè²»ãŒå°‘ãªããªã‚Šã¾ã™ã€‚å ´åˆã«
+ よã£ã¦ã¯10å€ä»¥ä¸Šlibmysqlã®ã»ã†ãŒå†—é•·ã«ãªã‚Šã¾ã™ã€‚
+
+ç¾åœ¨ã®ãƒãƒ¼ã‚¸ãƒ§ãƒ³ã§ã¯ä»¥ä¸‹ã®ã‚ˆã†ãªå‡¦ç†ã‚’サãƒãƒ¼ãƒˆã—ã¦ã„ã¾ã™ã€‚
+・指定ã•ã‚ŒãŸç´¢å¼•ã«ã¤ã„ã¦ã€æŒ‡å®šã•ã‚ŒãŸå€¤ã¨å®Œå…¨ä¸€è‡´ã™ã‚‹ã‚ˆã†ãªãƒ¬ã‚³ãƒ¼
+ ドをå–得。(SELECT ??? FROM tbl WHERE k1 = v1 AND k2 = v2...)。
+ 索引を使ã‚ãªã„検索ã¯ã‚µãƒãƒ¼ãƒˆã—ã¦ã„ã¾ã›ã‚“。
+・指定ã•ã‚ŒãŸç´¢å¼•ã«ã¤ã„ã¦ã€æŒ‡å®šã•ã‚ŒãŸå€¤ã®ä½ç½®ã®å‰å¾Œã®ãƒ¬ã‚³ãƒ¼ãƒ‰ã‚’å–
+ 得。(SELECT ??? FROM tbl WHERE k1 >= v1 LIMIT 100)
+・å‰è¿°ã®ã‚ˆã†ãªæ‰‹æ®µã§å–å¾—ã—ãŸãƒ¬ã‚³ãƒ¼ãƒ‰ã«å¯¾ã™ã‚‹UPDATEã¨DELETE
+・レコードã®INSERT
+
+以下ã®ã‚ˆã†ãªè¨€èªžã‚’サãƒãƒ¼ãƒˆã—ã¾ã™ã€‚
+・C++。libhsclientをリンクã—ã¾ã™ã€‚
+・Perl。Net::HandlerSocketã‚’useã—ã¾ã™ã€‚
+
+ç¾åœ¨ã®ãƒãƒ¼ã‚¸ãƒ§ãƒ³ã§ã¯GNU/Linuxã§ã®ã¿å‹•ä½œã—ã¾ã™ã€‚
+
+-----------------------------------------------------------------
+既知ã®å•é¡Œ
+
+・killã§handlersocketスレッドを殺ã™ã¨ã€ã‚¹ãƒ¬ãƒƒãƒ‰æ•°ãŒæ¸›ã£ãŸã¾ã¾å›žå¾©
+ ã—ã¾ã›ã‚“。
+
diff --git a/plugin/handler_socket/docs-ja/installation.ja.txt b/plugin/handler_socket/docs-ja/installation.ja.txt
new file mode 100644
index 00000000000..c14f47f6c02
--- /dev/null
+++ b/plugin/handler_socket/docs-ja/installation.ja.txt
@@ -0,0 +1,87 @@
+
+-----------------------------------------------------------------
+HandlerSocketプラグインã®ãƒ“ルド方法(RPMを使ã‚ãªã„方法)
+
+以下ã®ã‚ˆã†ã«ã—ã¦configureを実行ã—ã¾ã™ã€‚
+
+ $ ./autogen.sh
+ $ ./configure --with-mysql-source=/work/mysql-5.1.50 --with-mysql-bindir=/work/mysql-5.1.50-linux-x86_64-glibc23/bin --with-mysql-plugindir=/work/mysql-5.1.50-linux-x86_64-glibc23/lib/plugin
+
+ã“ã“ã§--with-mysql-sourceã«ã¯MySQLã®ã‚½ãƒ¼ã‚¹ã‚³ãƒ¼ãƒ‰ã®ãƒˆãƒƒãƒ—ディレク
+トリを指定ã—ã¾ã™ã€‚--with-mysql-bindirã«ã¯ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«æ¸ˆã¿ã®MySQL
+ã®mysql_configコマンドãŒæœ‰ã‚‹ãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒªã‚’指定ã—ã¾ã™ã€‚
+ãã®å¾Œä»¥ä¸‹ã®ã‚ˆã†ã«ãƒ“ルド・インストールã—ã¾ã™ã€‚
+
+ $ make
+ $ sudo make install
+
+-----------------------------------------------------------------
+クライアントライブラリã®ãƒ“ルド方法(RPMを使ã‚ãªã„方法)
+
+クライアントライブラリをビルドã™ã‚‹éš›ã«ã¯ã€MySQLã®ã‚½ãƒ¼ã‚¹ã‚³ãƒ¼ãƒ‰ã¯
+å¿…è¦ã‚ã‚Šã¾ã›ã‚“。ã¾ãŸMySQLãŒã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ã•ã‚Œã¦ã„ã‚‹å¿…è¦ã‚‚ã‚ã‚Šã¾ã›ã‚“。
+
+ $ ./autogen.sh
+ $ ./configure --disable-handlersocket-server
+ $ make
+ $ sudo make install
+ $ cd perl-Net-HandlerSocket
+ $ perl Makefile.PL
+ $ make
+ $ sudo make install
+
+-----------------------------------------------------------------
+ビルド方法(RPM)
+
+以下ã®ã‚ˆã†ã«å®Ÿè¡Œã™ã‚Œã°ã€rpmパッケージãŒãƒ“ルド&インストールã•ã‚Œã¾
+ã™ã€‚
+
+(MySQLサーãƒå´ã€HandlerSocketプラグインをインストールã™ã‚‹)
+ $ ./autogen.sh
+ $ ./configure --with-mysql-source=/work/mysql-5.1.50 --with-mysql-bindir=/work/mysql-5.1.50-linux-x86_64-glibc23/bin --with-mysql-plugindir=/work/mysql-5.1.50-linux-x86_64-glibc23/lib/plugin
+ $ make rpm_cli
+ $ sudo rpm -U dist/RPMS/*/libhsclient*.rpm
+ $ make rpm_c
+ $ sudo rpm -U dist/RPMS/*/handlersocket*.rpm
+
+(クライアントå´ã€ã‚¯ãƒ©ã‚¤ã‚¢ãƒ³ãƒˆãƒ©ã‚¤ãƒ–ラリをインストールã™ã‚‹)
+ $ ./autogen.sh
+ $ ./configure --disable-handlersocket-server
+ $ make rpm_cli
+ $ sudo rpm -U dist/RPMS/*/libhsclient*.rpm
+ $ make rpm_perl
+ $ sudo rpm -U dist/RPMS/*/perl-Net-HandlerSocket*.rpm
+
+-----------------------------------------------------------------
+èµ·å‹•
+
+mysqlã‚’èµ·å‹•ã—ãŸçŠ¶æ…‹ã§ã€mysqlã®è¨­å®šãƒ•ã‚¡ã‚¤ãƒ«(my.cnfç­‰)ã«ä»¥ä¸‹ã®å†…容を
+追加ã—ã¾ã™ã€‚
+
+ [mysqld]
+ handlersocket_port = 9998
+ # handlersocketãŒæŽ¥ç¶šã‚’å—ã‘付ã‘ã‚‹ãƒãƒ¼ãƒˆ(å‚照系リクエスト用)
+ handlersocket_port_wr = 9999
+ # handlersocketãŒæŽ¥ç¶šã‚’å—ã‘付ã‘ã‚‹ãƒãƒ¼ãƒˆ(更新系リクエスト用)
+ handlersocket_address =
+ # handlersocketãŒãƒã‚¤ãƒ³ãƒ‰ã™ã‚‹ã‚¢ãƒ‰ãƒ¬ã‚¹(空ã®ã¾ã¾ã§OK)
+ handlersocket_verbose = 0
+ # デãƒãƒƒã‚°ç”¨
+ handlersocket_timeout = 300
+ # 通信タイムアウト(秒)
+ handlersocket_threads = 16
+ # handlersocketã®ãƒ¯ãƒ¼ã‚«ãƒ¼ã‚¹ãƒ¬ãƒƒãƒ‰æ•°
+ thread_concurrency = 128
+ # handlersocketãŒå¹¾ã¤ã‹ã®ã‚¹ãƒ¬ãƒƒãƒ‰ã‚’å æœ‰ã™ã‚‹ãŸã‚ã€å¤§ãã‚ã®
+ # 値を指定ã—ã¦ãã ã•ã„
+ open_files_limit = 65535
+ # ソケットを大é‡ã«é–‹ã‘るよã†ã«ã™ã‚‹ãŸã‚ã€å¤§ãã‚ã®å€¤ã‚’指定ã—
+ # ã¦ãã ã•ã„
+
+以下ã®ã‚¯ã‚¨ãƒªã‚’実行ã—ã¾ã™ã€‚
+
+ mysql> install plugin handlersocket soname 'handlersocket.so';
+ Query OK, 0 rows affected (0.06 sec)
+
+以上ã§handlersocketã¸ã‚¯ãƒ©ã‚¤ã‚¢ãƒ³ãƒˆã‹ã‚‰ã‚¢ã‚¯ã‚»ã‚¹ã§ãるよã†ã«ãªã‚Šã¾ã™ã€‚
+
diff --git a/plugin/handler_socket/docs-ja/perl-client.ja.txt b/plugin/handler_socket/docs-ja/perl-client.ja.txt
new file mode 100644
index 00000000000..5d3adfa3301
--- /dev/null
+++ b/plugin/handler_socket/docs-ja/perl-client.ja.txt
@@ -0,0 +1,118 @@
+
+-----------------------------------------------------------------
+handlersocketプラグインã¸ã®æŽ¥ç¶šã‚’é–‹ãã«ã¯ã€Net::HandlerSocketオブ
+ジェクトを作æˆã—ã¾ã™ã€‚
+
+ use Net::HandlerSocket;
+ my $args = { host => 'localhost', port => 9998 };
+ my $hs = new Net::HandlerSocket($args);
+
+-----------------------------------------------------------------
+検索ãªã©ã®å‘½ä»¤ã‚’実行ã™ã‚‹å‰ã«ã€å‡¦ç†å¯¾è±¡ã¨ãªã‚‹ç´¢å¼•ã‚’é–‹ãå¿…è¦ãŒã‚ã‚Š
+ã¾ã™ã€‚
+
+ my $err = $hs->open_index(3, 'database1', 'table1', 'PRIMARY',
+ 'f1,f2');
+ die $hs->get_error() if $res->[0] != 0;
+
+最åˆã®å¼•æ•°ã¯é–‹ã索引ã«ä»˜ã‘る番å·ã§ã™ã€‚付ã‘ãŸç•ªå·ã¯åŒä¸€ã®
+Net::HandlerSocketオブジェクトã«ã¤ã„ã¦ã®ã¿æœ‰åŠ¹ã§ã™ã€‚第4引数ã¯é–‹ã
+索引ã®åå‰ã§ã€ã€ŒPRIMARYã€ãŒæŒ‡å®šã•ã‚Œå ´åˆã¯ãƒ—ライマリキーãŒé–‹ã‹ã‚Œã¾
+ã™ã€‚第5引数ã¯ã€Œ,ã€ã§åŒºåˆ‡ã‚‰ã‚ŒãŸåˆ—åã®ãƒªã‚¹ãƒˆã§ã™ã€‚
+
+-----------------------------------------------------------------
+テーブルã‹ã‚‰ç´¢å¼•ã‚’使ã£ã¦è¡Œã‚’å–å¾—ã™ã‚‹ã«ã¯ã€execute_singleメソッド
+を呼ã³ã¾ã™ã€‚
+
+ my $res = $hs->execute_single(3, '=', [ 'foo' ], 1, 0);
+ die $hs->get_error() if $res->[0] != 0;
+ shift(@$res);
+
+最åˆã®å¼•æ•°ã¯ç´¢å¼•ã®ç•ªå·ã§ã€åŒã˜Net::HandlerSocketオブジェクトã¸
+open_indexã§ä»˜ã‘ãŸã‚‚ã®ã§ãªã‘ã‚Œã°ãªã‚Šã¾ã›ã‚“。第2引数ã«ã¯æ“作を指定
+ã—ã¾ã™ã€‚ç¾åœ¨ã®ãƒãƒ¼ã‚¸ãƒ§ãƒ³ã§ã¯ã€ã€Œ=ã€ã€ã€Œ>=ã€ã€ã€Œ<=ã€ã€ã€Œ>ã€ã€ã€Œ<ã€
+ã®æ“作ãŒåˆ©ç”¨å¯èƒ½ã§ã™ã€‚第3引数ã¯é…列ã¸ã®å‚ç…§ã§ã€ã“ã‚Œã¯æŽ¢ã™ã¹ãè¡Œã®
+キー値を指定ã—ã¾ã™ã€‚é…列ã®é•·ã•ã¯ç´¢å¼•ã®ã‚­ãƒ¼ã®å€‹æ•°ã¨åŒã˜ã‹å°‘ãªã„æ•°
+ã§ãªã‘ã‚Œã°ãªã‚Šã¾ã›ã‚“。第4引数ã¨ç¬¬5引数ã¯ãã‚Œãžã‚Œã€å–å¾—ã™ã‚‹æœ€å¤§è¡Œ
+æ•°ã€å–å¾—å‰ã«èª­ã¿é£›ã°ã™è¡Œæ•°ã‚’指定ã—ã¾ã™ã€‚å–å¾—ã•ã‚Œã‚‹åˆ—ã¯å¯¾å¿œã™ã‚‹
+open_index呼ã³å‡ºã—ã®ç¬¬5引数ã§æŒ‡å®šã•ã‚ŒãŸã‚‚ã®ã«ãªã‚Šã¾ã™ã€‚
+
+execute_singleメソッドã¯å¸¸ã«é…列ã¸ã®å‚照を返ã—ã¾ã™ã€‚最åˆã®è¦ç´ ã¯
+エラーコードã§ã€ã“ã‚ŒãŒ0ãªã‚‰ã°æˆåŠŸã‚’表ã—ã¾ã™ã€‚残りã®è¦ç´ ã¯åˆ—ã®å€¤ã§
+ã™ã€‚ã‚‚ã—å–å¾—ã•ã‚ŒãŸãƒ‡ãƒ¼ã‚¿ãŒè¤‡æ•°è¡Œã®å ´åˆã¯ã€ãã‚ŒãŒä¸€ã¤ã®é…列ã¸é€£çµ
+ã•ã‚ŒãŸå½¢ã§æ ¼ç´ã•ã‚Œã¦ã„ã¾ã™ã€‚例ãˆã°ã€5è¡Œ3列ã®ãƒ‡ãƒ¼ã‚¿ã®å ´åˆã€æ¬¡ã®ã‚ˆ
+ã†ãªã‚³ãƒ¼ãƒ‰ã«ã‚ˆã£ã¦ãã®å†…容をå–å¾—ã§ãã¾ã™ã€‚
+
+ die $hs->get_error() if $res->[0] != 0;
+ shift(@$res);
+ for (my $row = 0; $row < 5; ++$row) {
+ for (my $col = 0; $col < 3; ++$col) {
+ my $value = $res->[$row * 5 + $col];
+ # ...
+ }
+ }
+
+-----------------------------------------------------------------
+行を更新ã¾ãŸã¯å‰Šé™¤ã™ã‚‹ã«ã¯ã€æ›´ã«å¤šãã®å¼•æ•°ã‚’指定ã—ã¦
+execute_singleメソッドを呼ã³å‡ºã—ã¾ã™ã€‚書ãè¾¼ã¿å‡¦ç†ã‚’ã™ã‚‹ã«ã¯ã€
+対象ã®Net::HandlerSocketオブジェクトã¯æ›´æ–°ç”¨handlersocketワーカ(æ—¢
+定ã§ã¯ãƒãƒ¼ãƒˆ9999)ã¸æŽ¥ç¶šã•ã‚ŒãŸã‚‚ã®ã§ãªãã¦ã¯ãªã‚Šã¾ã›ã‚“。
+(安全ã®ãŸã‚ã€ãƒãƒ¼ãƒˆ9998ã¯å‚照処ç†ã ã‘ã‚’å—ã‘付ã‘ã€ãƒãƒ¼ãƒˆ9999ã¯æ›´æ–°
+処ç†ã‚‚å—ã‘付ã‘るよã†ã«ãªã£ã¦ã„ã¾ã™ã€‚ãƒãƒ¼ãƒˆ9999ã¯å‚照処ç†ã‚‚å—ã‘付
+ã‘ã¾ã™ãŒã€ãƒ¬ã‚³ãƒ¼ãƒ‰ãƒ­ãƒƒã‚¯ç­‰ã®å½±éŸ¿ã§é…ããªã‚Šã¾ã™ã€‚ãƒãƒ¼ãƒˆç•ªå·ã¯
+mysqldã®ã€Œhandlersocket_portã€ã¨ã€Œhandlersocket_port_wrã€ã®è¨­å®šé …
+ç›®ã§å¤‰æ›´ã§ãã¾ã™ã€‚)
+
+ my $args = { host => 'localhost', port => 9999 };
+ my $hs = new Net::HandlerSocket($args);
+
+ my $res = $hs->execute_single(3, '=', [ 'bar' ], 1, 0, 'U',
+ [ 'fubar', 'hoge' ]);
+ die $hs->get_error() if $res->[0] != 0;
+ my $num_updated_rows = $res->[1];
+
+ my $res = $hs->execute_single(3, '=', [ 'baz' ], 1, 0, 'D');
+ die $hs->get_error() if $res->[0] != 0;
+ my $num_deleted_rows = $res->[1];
+
+execute_singleã®ç¬¬6引数ã¯å¤‰æ›´å‡¦ç†ã®ç¨®é¡žã‚’指定ã—ã¾ã™ã€‚ç¾åœ¨ã®ãƒãƒ¼
+ジョンã§ã¯ã€ŒUã€ã¨ã€ŒDã€ãŒåˆ©ç”¨å¯èƒ½ã§ã™ã€‚「Uã€ã«ã¤ã„ã¦ã¯ã€ç¬¬7引数ã§
+æ–°ã—ã„値を指定ã—ã¾ã™ã€‚æ›´æ–°ã•ã‚Œã‚‹åˆ—ã¯ã€å¯¾å¿œã™ã‚‹open_index呼ã³å‡ºã—
+ã®ç¬¬5引数ã§æŒ‡å®šã•ã‚ŒãŸã‚‚ã®ã«ãªã‚Šã¾ã™ã€‚「Dã€ã«ã¤ã„ã¦ã¯ç¬¬7引数ã¯çœ
+ç•¥ã§ãã¾ã™ã€‚
+
+-----------------------------------------------------------------
+execute_singleメソッドã¯åˆ—ã®æŒ¿å…¥ã«ã‚‚使用ã§ãã¾ã™ã€‚
+
+ my $res = $hs->execute_single(3, '+', [ 'foo', 'bar', 'baz' ]);
+ die $hs->get_error() if $res->[0] != 0;
+ my $num_inserted_rows = $res->[1];
+
+第3引数ã¯ã€å¯¾å¿œã™ã‚‹open_index呼ã³å‡ºã—ã®ç¬¬5引数ã®åˆ—リストã¨åŒã˜ã 
+ã‘ã®é•·ã•ã®é…列ã¸ã®å‚ç…§ã§ãªã‘ã‚Œã°ãªã‚Šã¾ã›ã‚“。open_index呼ã³å‡ºã—ã®
+第5引数ã«æŒ‡å®šã•ã‚Œã¦ã„ãªã„列ã«ã¤ã„ã¦ã¯ã€ãã®åˆ—ã®æ—¢å®šå€¤ãŒã‚»ãƒƒãƒˆã•ã‚Œ
+ã¾ã™ã€‚
+
+-----------------------------------------------------------------
+execute_multiメソッドを使ãˆã°ã€è¤‡æ•°ã®ãƒªã‚¯ã‚¨ã‚¹ãƒˆã‚’一ã¤ã®å‘¼ã³å‡ºã—ã§
+実行ã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚ã“ã‚Œã¯ãƒªã‚¯ã‚¨ã‚¹ãƒˆã‚’個別ã«å®Ÿè¡Œã™ã‚‹ã‚ˆã‚Šé«˜é€Ÿ
+ã§ã™ã€‚
+
+ my $rarr = $hs->execute_multi([
+ [ 0, '>=', [ 'foo' ], 5, 0 ],
+ [ 2, '=', [ 'bar' ], 1, 0 ],
+ [ 4, '<', [ 'baz' ], 10, 5 ],
+ ]);
+ for my $res (@$rarr) {
+ die $hs->get_error() if $res->[0] != 0;
+ shift(@$res);
+ # ...
+ }
+
+-----------------------------------------------------------------
+エラーãŒèµ·ã“ã‚‹ã¨è¿”値ã®é…列å‚ç…§ã®æœ€åˆã®è¦ç´ ãŒ0以外ã«ãªã‚Šã¾ã™ã€‚è² ã®
+æ•°ã®å ´åˆã¯I/OエラーãŒèµ·ã“ã£ãŸã“ã¨ã‚’示ã—ã€ãã®å ´åˆã¯ãã®
+Net::HandlerSocketオブジェクトã¯ç ´æ£„ã™ã‚‹ã¹ãã§ã™ã€‚æ­£ã®å€¤ã®å ´åˆã¯
+接続ã¯ç¶­æŒã•ã‚Œã¦ã„ã‚‹ãŸã‚ã€ãã®ã‚ªãƒ–ジェクトã¯ãれ以後もå†åˆ©ç”¨ã§ã
+ã¾ã™ã€‚
+
diff --git a/plugin/handler_socket/docs-ja/protocol.ja.txt b/plugin/handler_socket/docs-ja/protocol.ja.txt
new file mode 100644
index 00000000000..01c9d39f71f
--- /dev/null
+++ b/plugin/handler_socket/docs-ja/protocol.ja.txt
@@ -0,0 +1,94 @@
+
+-----------------------------------------------------------------
+handlersocketã®é€šä¿¡ãƒ—ロトコル
+
+-----------------------------------------------------------------
+構文
+
+・コマンド行ã¯æ”¹è¡Œ(LF)ã§çµ‚ã‚る。
+・コマンド行ã¯è¤‡æ•°ã®ãƒˆãƒ¼ã‚¯ãƒ³ã‹ã‚‰ãªã‚Šã€ãƒˆãƒ¼ã‚¯ãƒ³é–“ã¯TABã§åŒºåˆ‡ã‚‰ã‚Œã‚‹ã€‚
+・トークンã¯NULLトークンã‹ã€æ–‡å­—列トークンã®ã„ãšã‚Œã‹ã€‚
+・NULLトークンã¯å˜ä¸€ã®NUL文字ã§ã‚らã‚ã•ã‚Œã‚‹ã€‚
+・文字列トークンã¯ã€0ãƒã‚¤ãƒˆä»¥ä¸Šã®æ–‡å­—列ã§ã‚らã‚ã•ã‚Œã‚‹ã€‚ãŸã ã—0x10
+ 未満ã®æ–‡å­—ã«ã¤ã„ã¦ã¯0x01ã‚’å‰ç½®ã—ã€0x40を加ãˆãŸã‚³ãƒ¼ãƒ‰ã§ã‚らã‚ã•
+ れる。ãれ以外ã®æ–‡å­—ã¯ãã®æ–‡å­—自身ã®ã‚³ãƒ¼ãƒ‰ã§ã‚らã‚ã•ã‚Œã‚‹ã€‚
+
+-----------------------------------------------------------------
+リクエストã¨ãƒ¬ã‚¹ãƒãƒ³ã‚¹
+
+・接続ãŒç¢ºç«‹ã—ãŸç›´å¾Œã®çŠ¶æ…‹ã§ã¯ã€ã¾ãšã‚¯ãƒ©ã‚¤ã‚¢ãƒ³ãƒˆãŒã‚³ãƒžãƒ³ãƒ‰è¡Œã‚’é€
+ る。(リクエスト)
+・サーãƒã¯ã‚¯ãƒ©ã‚¤ã‚¢ãƒ³ãƒˆãŒé€ã£ãŸãƒªã‚¯ã‚¨ã‚¹ãƒˆã¨ä¸åº¦åŒã˜æ•°ã®ã‚³ãƒžãƒ³ãƒ‰è¡Œ
+ ã‚’è¿”ã™ã€‚(レスãƒãƒ³ã‚¹)
+・リクエストã¯ãƒ‘イプライン化ã—ã¦ã‚ˆã„。ã¤ã¾ã‚Šã‚¯ãƒ©ã‚¤ã‚¢ãƒ³ãƒˆã¯å‰ã«
+ é€ã£ãŸãƒªã‚¯ã‚¨ã‚¹ãƒˆã«å¯¾ã™ã‚‹è¿”事を待ãŸãšã«æ¬¡ã®ãƒªã‚¯ã‚¨ã‚¹ãƒˆã‚’é€ã£ã¦ã‚‚
+ よã„。
+
+-----------------------------------------------------------------
+リクエスト
+
+・open_index命令ã¯æ¬¡ã®ã‚ˆã†ãªæ§‹æ–‡ã‚’æŒã¤ã€‚
+ 'P' indexid dbname tablename indexname fieldlist
+ indexidã¯é–‹ã„ã¦ã„る索引ã«ä»˜ã‘られる番å·ã§ã€åŒä¸€æŽ¥ç¶šä¸Šã§å¾Œã«å®Ÿè¡Œ
+ ã™ã‚‹å‘½ä»¤ã®ã€å¯¾è±¡ç´¢å¼•ã‚’指定ã™ã‚‹ãŸã‚ã«ä½¿ã‚れる。dbnameã€tablenameã€
+ indexnameã¯ãã‚Œãžã‚Œé–‹ããŸã„DBã€ãƒ†ãƒ¼ãƒ–ルã€ç´¢å¼•ã®åå‰ã€‚索引ã®åå‰
+ ã¨ã—ã¦"PRIMARY"を指定ã™ã‚‹ã¨ãƒ—ライマリキーãŒé–‹ã‹ã‚Œã‚‹ã€‚fieldlist
+ ã¯ã‚«ãƒ³ãƒžåŒºåˆ‡ã‚Šã®åˆ—åã®ãƒªã‚¹ãƒˆã€‚
+・find命令ã¯æ¬¡ã®ã‚ˆã†ãªæ§‹æ–‡ã‚’æŒã¤ã€‚
+ indexid op nflds v1 ... vn limit offset
+ indexidã¯å®Ÿè¡Œå¯¾è±¡ã®ç´¢å¼•ã‚’指定ã™ã‚‹ã€‚opã¯ç´¢å¼•æ¤œç´¢ã®æ¼”ç®—å­(後述)。
+ v1ã‹ã‚‰vnã¯å¯å¤‰é•·ã§ã€ãã®å€‹æ•°ã¯nflds。nfldsã¯indexidã§æŒ‡å®šã•ã‚ŒãŸ
+ open_index命令ã®indexnameã®ç´¢å¼•ã®fieldlistã®ãƒ•ã‚£ãƒ¼ãƒ«ãƒ‰æ•°ã«ç­‰ã—
+ ã„ã‹å°ã•ããªãã¦ã¯ãªã‚‰ãªã„。m2ã‹ã‚‰mkã¯å¯å¤‰é•·ã§ã€ãã®å€‹æ•°ã¯
+ indexidã§æŒ‡å®šã•ã‚ŒãŸopen_index命令ãŒç™ºè¡Œã•ã‚ŒãŸéš›ã®fieldlistã«ä¸€
+ 致ã—ãªã‘ã‚Œã°ãªã‚‰ãªã„。コマンド行ã®limit以é™ã¯çœç•¥ã§ãる。limit
+ ã¨offsetã¯ã€æ¤œç´¢æ¡ä»¶ã«åˆè‡´ã™ã‚‹åˆ—ã®ã†ã¡ãƒ¬ã‚¹ãƒãƒ³ã‚¹ã«è¿”ã™åˆ—æ•°ã®ä¸Š
+ é™ã¨ã€ã‚¹ã‚­ãƒƒãƒ—ã™ã‚‹åˆ—数。limitã¨offsetã‚’çœç•¥ã—ãŸå ´åˆã¯ãã‚Œãžã‚Œ1
+ ã¨0ãŒæŒ‡å®šã•ã‚ŒãŸã¨ãã¨åŒã˜å‹•ä½œã‚’ã™ã‚‹ã€‚find命令ã¯ãƒ¬ã‚¹ãƒãƒ³ã‚¹ã¨ã—ã¦ã€
+ æ¡ä»¶ã«åˆè‡´ã—ãŸåˆ—ã®ãƒªã‚¹ãƒˆã‚’è¿”ã™ã€‚opã¨ã—ã¦æŒ‡å®šã§ãる演算å­ã¯æ¬¡ã®
+ ã¨ãŠã‚Šã€‚
+ '=' - v1 ... vnã¨ä¸€è‡´ã™ã‚‹ã‚‚ã®ã‚’å–å¾—
+ '>' - v1 ... vnより大ãã„ã‚‚ã®ã‚’昇順ã«å–å¾—
+ '>=' - v1 ... vnã«ä¸€è‡´ã™ã‚‹ã‹å¤§ãã„ã‚‚ã®ã‚’昇順ã«å–å¾—
+ '<' - v1 ... vnよりå°ã•ã„ã‚‚ã®ã‚’é™é †ã«å–å¾—
+ '<=' - v1 ... vnã«ä¸€è‡´ã™ã‚‹ã‹ç­‰ã—ã„ã‚‚ã®ã‚’é™é †ã«å–å¾—
+ nfldsãŒ1より大ãã„(v1 ... vnãŒ2個以上)ã¨ãã¯è¾žæ›¸å¼é †åºã§æ¯”較ã•
+ れる。
+・find_modify命令ã¯æ¬¡ã®ã‚ˆã†ãªæ§‹æ–‡ã‚’æŒã¤ã€‚
+ indexid op nflds v1 ... vn limit offset modop m1 ... mk
+ modopよりå‰ã®éƒ¨åˆ†ã¯find命令ã¨åŒç­‰ã§ã€ã“ã‚Œã«ã‚ˆã£ã¦æ“作対象ã®è¡Œã‚’
+ 指定ã™ã‚‹ã€‚ãã®æ“作対象ã®è¡Œã«å¯¾ã—modopã§æŒ‡å®šã•ã‚ŒãŸå¤‰æ›´å‡¦ç†ã‚’実行
+ ã™ã‚‹ã€‚m1 ... mkã¯å¯å¤‰é•·ã§ã€çœç•¥ã§ãる。modopã¯æ¬¡ã„ãšã‚Œã‹ã€‚
+ 'U' - indexidã§æŒ‡å®šã•ã‚ŒãŸopen_index命令ã®fieldlist列
+ ã®å†…容をã€m1 ... mkã®å€¤ã§æ›´æ–°ã™ã‚‹ã€‚
+ 'D' - 対象ã®è¡Œã‚’削除ã™ã‚‹ã€‚m1 ... mkã®å€¤ã¯ç„¡è¦–ã•ã‚Œã‚‹ã€‚
+・insert命令ã¯ã®ã‚ˆã†ãªæ§‹æ–‡ã‚’æŒã¤ã€‚
+ indexid '+' nflds v1 ... vn
+ indexidã§æŒ‡å®šã•ã‚ŒãŸãƒ†ãƒ¼ãƒ–ルã«ã€åˆ—を挿入ã™ã‚‹ã€‚v1 ... vnã¯å¯å¤‰é•·
+ ã§ã€ãã®å€‹æ•°ã¯nflds。nfldsã¯indexidã§æŒ‡å®šã•ã‚ŒãŸopen_index命令ã®
+ indexnameã®ç´¢å¼•ã®fieldlistã®ãƒ•ã‚£ãƒ¼ãƒ«ãƒ‰æ•°ã«ç­‰ã—ã„ã‹å°ã•ããªãã¦
+ ã¯ãªã‚‰ãªã„。
+
+-----------------------------------------------------------------
+レスãƒãƒ³ã‚¹
+
+・open_index命令ãŒæˆåŠŸã—ãŸã¨ãã€ãƒ¬ã‚¹ãƒãƒ³ã‚¹ã¯æ¬¡ã®æ§‹æ–‡ã‚’æŒã¤ã€‚
+ '0' '1'
+・find命令ãŒæˆåŠŸã—ãŸã¨ãã€ãƒ¬ã‚¹ãƒãƒ³ã‚¹ã¯æ¬¡ã®æ§‹æ–‡ã‚’æŒã¤ã€‚
+ '0' nflds v1 ... vn
+ nfldsã¯çµæžœã‚»ãƒƒãƒˆã®åˆ—ã®æ•°ã‚’ã‚らã‚ã™ã€‚v1 ... vnã¯å¯å¤‰é•·ã§ã€ãã®
+ é•·ã•ã¯nfldsã®æ•´æ•°å€ã€‚v1 ... vnã¯ç©ºã®ã“ã¨ã‚‚ã‚ã‚Šã€ãã‚Œã¯æ¡ä»¶ã«åˆ
+ 致ã™ã‚‹ãƒ¬ã‚³ãƒ¼ãƒ‰ãŒå­˜åœ¨ã—ãªã‹ã£ãŸã“ã¨ã‚’ã‚らã‚ã™ã€‚çµæžœã‚»ãƒƒãƒˆãŒè¤‡æ•°
+ è¡Œã«ãªã£ãŸã¨ãã¯v1 ... vnã®é•·ã•ãŒnfldsã®2å€ä»¥ä¸Šã¨ãªã‚Šã€æœ€åˆã®
+ è¡Œã‹ã‚‰é †ã«v1 ... vnã«ã‚»ãƒƒãƒˆã•ã‚Œã‚‹ã€‚
+・modify命令ãŒæˆåŠŸã—ãŸã¨ãã€ãƒ¬ã‚¹ãƒãƒ³ã‚¹ã¯æ¬¡ã®æ§‹æ–‡ã‚’æŒã¤ã€‚
+ '0' '1' nummod
+ nummodã¯å¤‰æ›´ãŒæ–½ã•ã‚ŒãŸè¡Œæ•°ã€‚nummodãŒ0ã®ã¨ãã¯å¤‰æ›´ã•ã‚ŒãŸè¡ŒãŒç„¡
+ ã‹ã£ãŸã“ã¨ã‚’ã‚らã‚ã™ã€‚
+・insert命令ãŒæˆåŠŸã—ãŸã¨ãã€ãƒ¬ã‚¹ãƒãƒ³ã‚¹ã¯æ¬¡ã®æ§‹æ–‡ã‚’æŒã¤ã€‚
+ '0' '1'
+・命令ãŒå¤±æ•—ã—ãŸã¨ãã€ãƒ¬ã‚¹ãƒãƒ³ã‚¹ã¯å‘½ä»¤ã«é–¢ã‚らãšæ¬¡ã®æ§‹æ–‡ã‚’æŒã¤ã€‚
+ err '1' message
+ errã¯0以外ã®æ•°å€¤ã§ã€ã‚¨ãƒ©ãƒ¼ã‚³ãƒ¼ãƒ‰ã‚’ã‚らã‚ã™ã€‚messageã¯äººé–“å¯èª­ãª
+ エラーメッセージ。ãŸã ã—messageãŒç„¡ã„ã“ã¨ã‚‚ã‚る。
+
diff --git a/plugin/handler_socket/handlersocket/COPYRIGHT.txt b/plugin/handler_socket/handlersocket/COPYRIGHT.txt
new file mode 100644
index 00000000000..41dda1279e2
--- /dev/null
+++ b/plugin/handler_socket/handlersocket/COPYRIGHT.txt
@@ -0,0 +1,27 @@
+
+ Copyright (c) 2010 DeNA Co.,Ltd.
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of DeNA Co.,Ltd. nor the names of its contributors
+ may be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY DeNA Co.,Ltd. "AS IS" AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ EVENT SHALL DeNA Co.,Ltd. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
diff --git a/plugin/handler_socket/handlersocket/Makefile.am b/plugin/handler_socket/handlersocket/Makefile.am
new file mode 100644
index 00000000000..f8e695b67f7
--- /dev/null
+++ b/plugin/handler_socket/handlersocket/Makefile.am
@@ -0,0 +1,11 @@
+CXXFLAGS += -fimplicit-templates
+pkgplugindir = $(PLUGIN_DIR)
+noinst_HEADERS = database.hpp hstcpsvr.hpp hstcpsvr_worker.hpp mysql_incl.hpp
+pkgplugin_LTLIBRARIES = handlersocket.la
+handlersocket_la_LDFLAGS = -module ../libhsclient/libhsclient.la
+handlersocket_la_CFLAGS = $(MYSQL_INC) $(MYSQL_CFLAGS) $(AM_CFLAGS) \
+ -I$(srcdir)/../libhsclient
+handlersocket_la_CXXFLAGS = $(MYSQL_INC) $(MYSQL_CFLAGS) $(AM_CFLAGS) \
+ -I$(srcdir)/../libhsclient
+handlersocket_la_SOURCES = database.cpp handlersocket.cpp \
+ hstcpsvr_worker.cpp hstcpsvr.cpp
diff --git a/plugin/handler_socket/handlersocket/Makefile.plain.template b/plugin/handler_socket/handlersocket/Makefile.plain.template
new file mode 100644
index 00000000000..4d5f8c102ff
--- /dev/null
+++ b/plugin/handler_socket/handlersocket/Makefile.plain.template
@@ -0,0 +1,31 @@
+
+MYSQL_INC = HANDLERSOCKET_MYSQL_INC
+MYSQL_LIB = HANDLERSOCKET_MYSQL_LIB
+
+CXX = g++ -Wall -g -fno-rtti -fno-exceptions -fPIC -DPIC
+LIBS = $(MYSQL_LIB) -lhsclient -lpthread -lz
+CXXFLAGS = -I/usr/include/handlersocket $(MYSQL_INC)
+LDFLAGS =
+
+CXXFLAGS += -O3 -DNDEBUG
+
+HANDLERSOCKET_OBJS = database.o hstcpsvr.o hstcpsvr_worker.o
+
+all: handlersocket.so
+
+handlersocket.so: $(HANDLERSOCKET_OBJS) handlersocket.cpp
+ $(CXX) $(CXXFLAGS) -fno-strict-aliasing -shared $^ -o $@ $(LDFLAGS) \
+ -Wl,-soname -Wl,$@ $(LIBS)
+clean:
+ rm -f *.a *.so *.o
+
+LIBDIR = $(shell \
+ if [ -e /usr/lib64/mysql ]; then echo /usr/lib64; else echo /usr/lib; fi)
+
+install: handlersocket.so
+ sudo sh -c 'ulimit -c unlimited ; /etc/init.d/mysql stop ; \
+ cp handlersocket.so handlersocket.so.cpy && \
+ mv handlersocket.so.cpy \
+ $(LIBDIR)/mysql/plugin/handlersocket.so && \
+ /etc/init.d/mysql start'
+
diff --git a/plugin/handler_socket/handlersocket/database.cpp b/plugin/handler_socket/handlersocket/database.cpp
new file mode 100644
index 00000000000..2662790b89f
--- /dev/null
+++ b/plugin/handler_socket/handlersocket/database.cpp
@@ -0,0 +1,1143 @@
+
+// vim:sw=2:ai
+
+/*
+ * Copyright (C) 2010 DeNA Co.,Ltd.. All rights reserved.
+ * See COPYRIGHT.txt for details.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "database.hpp"
+#include "string_util.hpp"
+#include "escape.hpp"
+#include "mysql_incl.hpp"
+
+#define DBG_KEY(x)
+#define DBG_SHUT(x)
+#define DBG_LOCK(x)
+#define DBG_THR(x)
+#define DBG_CMP(x)
+#define DBG_FLD(x)
+#define DBG_FILTER(x)
+#define DBG_REFCNT(x)
+#define DBG_DELETED
+
+/* status variables */
+unsigned long long int open_tables_count;
+unsigned long long int close_tables_count;
+unsigned long long int lock_tables_count;
+unsigned long long int unlock_tables_count;
+unsigned long long int index_exec_count;
+
+namespace dena {
+
+prep_stmt::prep_stmt()
+ : dbctx(0), table_id(static_cast<size_t>(-1)),
+ idxnum(static_cast<size_t>(-1))
+{
+}
+prep_stmt::prep_stmt(dbcontext_i *c, size_t tbl, size_t idx,
+ const fields_type& rf, const fields_type& ff)
+ : dbctx(c), table_id(tbl), idxnum(idx), ret_fields(rf), filter_fields(ff)
+{
+ if (dbctx) {
+ dbctx->table_addref(table_id);
+ }
+}
+prep_stmt::~prep_stmt()
+{
+ if (dbctx) {
+ dbctx->table_release(table_id);
+ }
+}
+
+prep_stmt::prep_stmt(const prep_stmt& x)
+ : dbctx(x.dbctx), table_id(x.table_id), idxnum(x.idxnum),
+ ret_fields(x.ret_fields), filter_fields(x.filter_fields)
+{
+ if (dbctx) {
+ dbctx->table_addref(table_id);
+ }
+}
+
+prep_stmt&
+prep_stmt::operator =(const prep_stmt& x)
+{
+ if (this != &x) {
+ if (dbctx) {
+ dbctx->table_release(table_id);
+ }
+ dbctx = x.dbctx;
+ table_id = x.table_id;
+ idxnum = x.idxnum;
+ ret_fields = x.ret_fields;
+ filter_fields = x.filter_fields;
+ if (dbctx) {
+ dbctx->table_addref(table_id);
+ }
+ }
+ return *this;
+}
+
+struct database : public database_i, private noncopyable {
+ database(const config& c);
+ virtual ~database();
+ virtual dbcontext_ptr create_context(bool for_write) volatile;
+ virtual void stop() volatile;
+ virtual const config& get_conf() const volatile;
+ public:
+ int child_running;
+ private:
+ config conf;
+};
+
+struct tablevec_entry {
+ TABLE *table;
+ size_t refcount;
+ bool modified;
+ tablevec_entry() : table(0), refcount(0), modified(false) { }
+};
+
+struct expr_user_lock : private noncopyable {
+ expr_user_lock(THD *thd, int timeout)
+ : lck_key("handlersocket_wr", 16, &my_charset_latin1),
+ lck_timeout(timeout),
+ lck_func_get_lock(&lck_key, &lck_timeout),
+ lck_func_release_lock(&lck_key)
+ {
+ lck_key.fix_fields(thd, 0);
+ lck_timeout.fix_fields(thd, 0);
+ lck_func_get_lock.fix_fields(thd, 0);
+ lck_func_release_lock.fix_fields(thd, 0);
+ }
+ long long get_lock() {
+ return lck_func_get_lock.val_int();
+ }
+ long long release_lock() {
+ return lck_func_release_lock.val_int();
+ }
+ private:
+ Item_string lck_key;
+ Item_int lck_timeout;
+ Item_func_get_lock lck_func_get_lock;
+ Item_func_release_lock lck_func_release_lock;
+};
+
+struct dbcontext : public dbcontext_i, private noncopyable {
+ dbcontext(volatile database *d, bool for_write);
+ virtual ~dbcontext();
+ virtual void init_thread(const void *stack_botton,
+ volatile int& shutdown_flag);
+ virtual void term_thread();
+ virtual bool check_alive();
+ virtual void lock_tables_if();
+ virtual void unlock_tables_if();
+ virtual bool get_commit_error();
+ virtual void clear_error();
+ virtual void close_tables_if();
+ virtual void table_addref(size_t tbl_id);
+ virtual void table_release(size_t tbl_id);
+ virtual void cmd_open_index(dbcallback_i& cb, size_t pst_id, const char *dbn,
+ const char *tbl, const char *idx, const char *retflds,
+ const char *filflds);
+ virtual void cmd_exec_on_index(dbcallback_i& cb, const cmd_exec_args& args);
+ virtual void set_statistics(size_t num_conns, size_t num_active);
+ private:
+ int set_thread_message(const char *fmt, ...)
+ __attribute__((format (printf, 2, 3)));
+ bool parse_fields(TABLE *const table, const char *str,
+ prep_stmt::fields_type& flds);
+ void cmd_insert_internal(dbcallback_i& cb, const prep_stmt& pst,
+ const string_ref *fvals, size_t fvalslen);
+ void cmd_sql_internal(dbcallback_i& cb, const prep_stmt& pst,
+ const string_ref *fvals, size_t fvalslen);
+ void cmd_find_internal(dbcallback_i& cb, const prep_stmt& pst,
+ ha_rkey_function find_flag, const cmd_exec_args& args);
+ size_t calc_filter_buf_size(TABLE *table, const prep_stmt& pst,
+ const record_filter *filters);
+ bool fill_filter_buf(TABLE *table, const prep_stmt& pst,
+ const record_filter *filters, uchar *filter_buf, size_t len);
+ int check_filter(dbcallback_i& cb, TABLE *table, const prep_stmt& pst,
+ const record_filter *filters, const uchar *filter_buf);
+ void resp_record(dbcallback_i& cb, TABLE *const table, const prep_stmt& pst);
+ void dump_record(dbcallback_i& cb, TABLE *const table, const prep_stmt& pst);
+ int modify_record(dbcallback_i& cb, TABLE *const table,
+ const prep_stmt& pst, const cmd_exec_args& args, char mod_op,
+ size_t& modified_count);
+ private:
+ typedef std::vector<tablevec_entry> table_vec_type;
+ typedef std::pair<std::string, std::string> table_name_type;
+ typedef std::map<table_name_type, size_t> table_map_type;
+ private:
+ volatile database *const dbref;
+ bool for_write_flag;
+ THD *thd;
+ MYSQL_LOCK *lock;
+ bool lock_failed;
+ std::auto_ptr<expr_user_lock> user_lock;
+ int user_level_lock_timeout;
+ bool user_level_lock_locked;
+ bool commit_error;
+ std::vector<char> info_message_buf;
+ table_vec_type table_vec;
+ table_map_type table_map;
+ #if MYSQL_VERSION_ID >= 50505
+ MDL_request *mdl_request;
+ #else
+ void *mdl_request;
+ #endif
+};
+
+database::database(const config& c)
+ : child_running(1), conf(c)
+{
+}
+
+database::~database()
+{
+}
+
+dbcontext_ptr
+database::create_context(bool for_write) volatile
+{
+ return dbcontext_ptr(new dbcontext(this, for_write));
+}
+
+void
+database::stop() volatile
+{
+ child_running = false;
+}
+
+const config&
+database::get_conf() const volatile
+{
+ return const_cast<const config&>(conf);
+}
+
+database_ptr
+database_i::create(const config& conf)
+{
+ return database_ptr(new database(conf));
+}
+
+dbcontext::dbcontext(volatile database *d, bool for_write)
+ : dbref(d), for_write_flag(for_write), thd(0), lock(0), lock_failed(false),
+ user_level_lock_timeout(0), user_level_lock_locked(false),
+ commit_error(false), mdl_request(0)
+{
+ info_message_buf.resize(8192);
+ user_level_lock_timeout = d->get_conf().get_int("wrlock_timeout", 12);
+}
+
+dbcontext::~dbcontext()
+{
+}
+
+namespace {
+
+int
+wait_server_to_start(THD *thd, volatile int& shutdown_flag)
+{
+ int r = 0;
+ DBG_SHUT(fprintf(stderr, "HNDSOCK wsts\n"));
+ pthread_mutex_lock(&LOCK_server_started);
+ while (!mysqld_server_started) {
+ timespec abstime = { };
+ set_timespec(abstime, 1);
+ pthread_cond_timedwait(&COND_server_started, &LOCK_server_started,
+ &abstime);
+ pthread_mutex_unlock(&LOCK_server_started);
+ pthread_mutex_lock(&thd->mysys_var->mutex);
+ THD::killed_state st = thd->killed;
+ pthread_mutex_unlock(&thd->mysys_var->mutex);
+ DBG_SHUT(fprintf(stderr, "HNDSOCK wsts kst %d\n", (int)st));
+ pthread_mutex_lock(&LOCK_server_started);
+ if (st != THD::NOT_KILLED) {
+ DBG_SHUT(fprintf(stderr, "HNDSOCK wsts kst %d break\n", (int)st));
+ r = -1;
+ break;
+ }
+ if (shutdown_flag) {
+ DBG_SHUT(fprintf(stderr, "HNDSOCK wsts kst shut break\n"));
+ r = -1;
+ break;
+ }
+ }
+ pthread_mutex_unlock(&LOCK_server_started);
+ DBG_SHUT(fprintf(stderr, "HNDSOCK wsts done\n"));
+ return r;
+}
+
+}; // namespace
+
+void
+dbcontext::init_thread(const void *stack_bottom, volatile int& shutdown_flag)
+{
+ DBG_THR(fprintf(stderr, "HNDSOCK init thread\n"));
+ {
+ my_thread_init();
+ thd = new THD;
+ thd->thread_stack = (char *)stack_bottom;
+ DBG_THR(const size_t of = (char *)(&thd->thread_stack) - (char *)thd);
+ DBG_THR(fprintf(stderr, "thread_stack = %p sz=%zu of=%zu\n",
+ thd->thread_stack, sizeof(THD), of));
+ thd->store_globals();
+ thd->system_thread = static_cast<enum_thread_type>(1<<30UL);
+ const NET v = { 0 };
+ thd->net = v;
+ if (for_write_flag) {
+ #if MYSQL_VERSION_ID >= 50505
+ thd->variables.option_bits |= OPTION_BIN_LOG;
+ #else
+ thd->options |= OPTION_BIN_LOG;
+ #endif
+ safeFree(thd->db);
+ thd->db = 0;
+ thd->db = my_strdup("handlersocket", MYF(0));
+ }
+ my_pthread_setspecific_ptr(THR_THD, thd);
+ DBG_THR(fprintf(stderr, "HNDSOCK x0 %p\n", thd));
+ }
+ {
+ pthread_mutex_lock(&LOCK_thread_count);
+ thd->thread_id = thread_id++;
+ threads.append(thd);
+ ++thread_count;
+ pthread_mutex_unlock(&LOCK_thread_count);
+ }
+
+ DBG_THR(fprintf(stderr, "HNDSOCK init thread wsts\n"));
+ wait_server_to_start(thd, shutdown_flag);
+ DBG_THR(fprintf(stderr, "HNDSOCK init thread done\n"));
+
+ thd_proc_info(thd, &info_message_buf[0]);
+ set_thread_message("hs:listening");
+ DBG_THR(fprintf(stderr, "HNDSOCK x1 %p\n", thd));
+
+ #if MYSQL_VERSION_ID >= 50508
+ mdl_request = new(thd->mem_root) MDL_request;
+ mdl_request->init(MDL_key::TABLE, "", "",
+ for_write_flag ? MDL_SHARED_WRITE : MDL_SHARED_READ, MDL_STATEMENT);
+ #elif MYSQL_VERSION_ID >= 50505
+ mdl_request = MDL_request::create(MDL_key::TABLE, "", "",
+ for_write_flag ? MDL_SHARED_WRITE : MDL_SHARED_READ, thd->mem_root);
+ #endif
+
+ lex_start(thd);
+
+ user_lock.reset(new expr_user_lock(thd, user_level_lock_timeout));
+}
+
+int
+dbcontext::set_thread_message(const char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ const int n = vsnprintf(&info_message_buf[0], info_message_buf.size(),
+ fmt, ap);
+ va_end(ap);
+ return n;
+}
+
+void
+dbcontext::term_thread()
+{
+ DBG_THR(fprintf(stderr, "HNDSOCK thread end %p\n", thd));
+ unlock_tables_if();
+ my_pthread_setspecific_ptr(THR_THD, 0);
+ {
+ pthread_mutex_lock(&LOCK_thread_count);
+ delete thd;
+ thd = 0;
+ --thread_count;
+ pthread_mutex_unlock(&LOCK_thread_count);
+ my_thread_end();
+ }
+}
+
+bool
+dbcontext::check_alive()
+{
+ pthread_mutex_lock(&thd->mysys_var->mutex);
+ THD::killed_state st = thd->killed;
+ pthread_mutex_unlock(&thd->mysys_var->mutex);
+ DBG_SHUT(fprintf(stderr, "chk HNDSOCK kst %p %p %d %zu\n", thd, &thd->killed,
+ (int)st, sizeof(*thd)));
+ if (st != THD::NOT_KILLED) {
+ DBG_SHUT(fprintf(stderr, "chk HNDSOCK kst %d break\n", (int)st));
+ return false;
+ }
+ return true;
+}
+
+void
+dbcontext::lock_tables_if()
+{
+ if (lock_failed) {
+ return;
+ }
+ if (for_write_flag && !user_level_lock_locked) {
+ if (user_lock->get_lock()) {
+ user_level_lock_locked = true;
+ } else {
+ lock_failed = true;
+ return;
+ }
+ }
+ if (lock == 0) {
+ const size_t num_max = table_vec.size();
+ TABLE *tables[num_max ? num_max : 1]; /* GNU */
+ size_t num_open = 0;
+ for (size_t i = 0; i < num_max; ++i) {
+ if (table_vec[i].refcount > 0) {
+ tables[num_open++] = table_vec[i].table;
+ }
+ table_vec[i].modified = false;
+ }
+ #if MYSQL_VERSION_ID >= 50505
+ lock = thd->lock = mysql_lock_tables(thd, &tables[0], num_open, 0);
+ #else
+ bool need_reopen= false;
+ lock = thd->lock = mysql_lock_tables(thd, &tables[0], num_open,
+ MYSQL_LOCK_NOTIFY_IF_NEED_REOPEN, &need_reopen);
+ #endif
+ statistic_increment(lock_tables_count, &LOCK_status);
+ thd_proc_info(thd, &info_message_buf[0]);
+ DENA_VERBOSE(100, fprintf(stderr, "HNDSOCK lock tables %p %p %zu %zu\n",
+ thd, lock, num_max, num_open));
+ if (lock == 0) {
+ lock_failed = true;
+ DENA_VERBOSE(10, fprintf(stderr, "HNDSOCK failed to lock tables %p\n",
+ thd));
+ }
+ if (for_write_flag) {
+ #if MYSQL_VERSION_ID >= 50505
+ thd->set_current_stmt_binlog_format_row();
+ #else
+ thd->current_stmt_binlog_row_based = 1;
+ #endif
+ }
+ }
+ DBG_LOCK(fprintf(stderr, "HNDSOCK tblnum=%d\n", (int)tblnum));
+}
+
+void
+dbcontext::unlock_tables_if()
+{
+ if (lock != 0) {
+ DENA_VERBOSE(100, fprintf(stderr, "HNDSOCK unlock tables\n"));
+ if (for_write_flag) {
+ for (size_t i = 0; i < table_vec.size(); ++i) {
+ if (table_vec[i].modified) {
+ query_cache_invalidate3(thd, table_vec[i].table, 0);
+ /* invalidate immediately */
+ }
+ }
+ bool suc = true;
+ #if MYSQL_VERSION_ID >= 50505
+ suc = (trans_commit_stmt(thd) == 0);
+ #else
+ suc = (ha_autocommit_or_rollback(thd, 0) == 0);
+ #endif
+ if (!suc) {
+ commit_error = true;
+ DENA_VERBOSE(10, fprintf(stderr,
+ "HNDSOCK unlock tables: commit failed\n"));
+ }
+ }
+ mysql_unlock_tables(thd, lock);
+ lock = thd->lock = 0;
+ statistic_increment(unlock_tables_count, &LOCK_status);
+ }
+ if (user_level_lock_locked) {
+ if (user_lock->release_lock()) {
+ user_level_lock_locked = false;
+ }
+ }
+}
+
+bool
+dbcontext::get_commit_error()
+{
+ return commit_error;
+}
+
+void
+dbcontext::clear_error()
+{
+ lock_failed = false;
+ commit_error = false;
+}
+
+void
+dbcontext::close_tables_if()
+{
+ unlock_tables_if();
+ if (!table_vec.empty()) {
+ DENA_VERBOSE(100, fprintf(stderr, "HNDSOCK close tables\n"));
+ close_thread_tables(thd);
+ statistic_increment(close_tables_count, &LOCK_status);
+ table_vec.clear();
+ table_map.clear();
+ }
+}
+
+void
+dbcontext::table_addref(size_t tbl_id)
+{
+ table_vec[tbl_id].refcount += 1;
+ DBG_REFCNT(fprintf(stderr, "%p %zu %zu addref\n", this, tbl_id,
+ table_vec[tbl_id].refcount));
+}
+
+void
+dbcontext::table_release(size_t tbl_id)
+{
+ table_vec[tbl_id].refcount -= 1;
+ DBG_REFCNT(fprintf(stderr, "%p %zu %zu release\n", this, tbl_id,
+ table_vec[tbl_id].refcount));
+}
+
+void
+dbcontext::resp_record(dbcallback_i& cb, TABLE *const table,
+ const prep_stmt& pst)
+{
+ char rwpstr_buf[64];
+ String rwpstr(rwpstr_buf, sizeof(rwpstr_buf), &my_charset_bin);
+ const prep_stmt::fields_type& rf = pst.get_ret_fields();
+ const size_t n = rf.size();
+ for (size_t i = 0; i < n; ++i) {
+ uint32_t fn = rf[i];
+ Field *const fld = table->field[fn];
+ DBG_FLD(fprintf(stderr, "fld=%p %zu\n", fld, fn));
+ if (fld->is_null()) {
+ /* null */
+ cb.dbcb_resp_entry(0, 0);
+ } else {
+ fld->val_str(&rwpstr, &rwpstr);
+ const size_t len = rwpstr.length();
+ if (len != 0) {
+ /* non-empty */
+ cb.dbcb_resp_entry(rwpstr.ptr(), rwpstr.length());
+ } else {
+ /* empty */
+ static const char empty_str[] = "";
+ cb.dbcb_resp_entry(empty_str, 0);
+ }
+ }
+ }
+}
+
+void
+dbcontext::dump_record(dbcallback_i& cb, TABLE *const table,
+ const prep_stmt& pst)
+{
+ char rwpstr_buf[64];
+ String rwpstr(rwpstr_buf, sizeof(rwpstr_buf), &my_charset_bin);
+ const prep_stmt::fields_type& rf = pst.get_ret_fields();
+ const size_t n = rf.size();
+ for (size_t i = 0; i < n; ++i) {
+ uint32_t fn = rf[i];
+ Field *const fld = table->field[fn];
+ if (fld->is_null()) {
+ /* null */
+ cb.dbcb_resp_entry(0, 0);
+ fprintf(stderr, "NULL");
+ } else {
+ fld->val_str(&rwpstr, &rwpstr);
+ const std::string s(rwpstr.ptr(), rwpstr.length());
+ fprintf(stderr, "[%s]", s.c_str());
+ }
+ }
+ fprintf(stderr, "\n");
+}
+
+int
+dbcontext::modify_record(dbcallback_i& cb, TABLE *const table,
+ const prep_stmt& pst, const cmd_exec_args& args, char mod_op,
+ size_t& modified_count)
+{
+ if (mod_op == 'U') {
+ /* update */
+ handler *const hnd = table->file;
+ uchar *const buf = table->record[0];
+ store_record(table, record[1]);
+ const prep_stmt::fields_type& rf = pst.get_ret_fields();
+ const size_t n = rf.size();
+ for (size_t i = 0; i < n; ++i) {
+ const string_ref& nv = args.uvals[i];
+ uint32_t fn = rf[i];
+ Field *const fld = table->field[fn];
+ if (nv.begin() == 0) {
+ fld->set_null();
+ } else {
+ fld->set_notnull();
+ fld->store(nv.begin(), nv.size(), &my_charset_bin);
+ }
+ }
+ table_vec[pst.get_table_id()].modified = true;
+ const int r = hnd->ha_update_row(table->record[1], buf);
+ if (r != 0 && r != HA_ERR_RECORD_IS_THE_SAME) {
+ return r;
+ }
+ ++modified_count; /* TODO: HA_ERR_RECORD_IS_THE_SAME? */
+ } else if (mod_op == 'D') {
+ /* delete */
+ handler *const hnd = table->file;
+ table_vec[pst.get_table_id()].modified = true;
+ const int r = hnd->ha_delete_row(table->record[0]);
+ if (r != 0) {
+ return r;
+ }
+ ++modified_count;
+ } else if (mod_op == '+' || mod_op == '-') {
+ /* increment/decrement */
+ handler *const hnd = table->file;
+ uchar *const buf = table->record[0];
+ store_record(table, record[1]);
+ const prep_stmt::fields_type& rf = pst.get_ret_fields();
+ const size_t n = rf.size();
+ size_t i = 0;
+ for (i = 0; i < n; ++i) {
+ const string_ref& nv = args.uvals[i];
+ uint32_t fn = rf[i];
+ Field *const fld = table->field[fn];
+ if (fld->is_null() || nv.begin() == 0) {
+ continue;
+ }
+ const long long pval = fld->val_int();
+ const long long llv = atoll_nocheck(nv.begin(), nv.end());
+ /* TODO: llv == 0? */
+ long long nval = 0;
+ if (mod_op == '+') {
+ /* increment */
+ nval = pval + llv;
+ } else {
+ /* decrement */
+ nval = pval - llv;
+ if ((pval < 0 && nval > 0) || (pval > 0 && nval < 0)) {
+ break; /* don't modify */
+ }
+ if ((pval < 0) != (nval < 0)) {
+ nval = 0; /* crip */
+ }
+ }
+ fld->store(nval, false);
+ }
+ if (i == n) {
+ /* modify */
+ table_vec[pst.get_table_id()].modified = true;
+ const int r = hnd->ha_update_row(table->record[1], buf);
+ if (r != 0 && r != HA_ERR_RECORD_IS_THE_SAME) {
+ return r;
+ }
+ ++modified_count;
+ }
+ }
+ return 0;
+}
+
+void
+dbcontext::cmd_insert_internal(dbcallback_i& cb, const prep_stmt& pst,
+ const string_ref *fvals, size_t fvalslen)
+{
+ if (!for_write_flag) {
+ return cb.dbcb_resp_short(2, "readonly");
+ }
+ lock_tables_if();
+ if (lock == 0) {
+ return cb.dbcb_resp_short(2, "lock_tables");
+ }
+ if (pst.get_table_id() >= table_vec.size()) {
+ return cb.dbcb_resp_short(2, "tblnum");
+ }
+ TABLE *const table = table_vec[pst.get_table_id()].table;
+ handler *const hnd = table->file;
+ uchar *const buf = table->record[0];
+ empty_record(table);
+ memset(buf, 0, table->s->null_bytes); /* clear null flags */
+ Field **fld = table->field;
+ size_t i = 0;
+ for (; *fld && i < fvalslen; ++fld, ++i) {
+ if (fvals[i].begin() == 0) {
+ (*fld)->set_null();
+ } else {
+ (*fld)->store(fvals[i].begin(), fvals[i].size(), &my_charset_bin);
+ }
+ }
+ table->next_number_field = table->found_next_number_field;
+ const int r = hnd->ha_write_row(buf);
+ table->next_number_field = 0;
+ table_vec[pst.get_table_id()].modified = true;
+ return cb.dbcb_resp_short(r != 0 ? 1 : 0, "");
+}
+
+void
+dbcontext::cmd_sql_internal(dbcallback_i& cb, const prep_stmt& pst,
+ const string_ref *fvals, size_t fvalslen)
+{
+ if (fvalslen < 1) {
+ return cb.dbcb_resp_short(2, "syntax");
+ }
+ return cb.dbcb_resp_short(2, "notimpl");
+}
+
+void
+dbcontext::cmd_find_internal(dbcallback_i& cb, const prep_stmt& pst,
+ ha_rkey_function find_flag, const cmd_exec_args& args)
+{
+ const bool debug_out = (verbose_level >= 100);
+ bool need_resp_record = true;
+ char mod_op = 0;
+ const string_ref& mod_op_str = args.mod_op;
+ if (mod_op_str.size() != 0) {
+ if (!for_write_flag) {
+ return cb.dbcb_resp_short(2, "readonly");
+ }
+ mod_op = mod_op_str.begin()[0];
+ need_resp_record = mod_op_str.size() > 1 && mod_op_str.begin()[1] == '?';
+ switch (mod_op) {
+ case 'U': /* update */
+ case 'D': /* delete */
+ case '+': /* increment */
+ case '-': /* decrement */
+ break;
+ default:
+ if (debug_out) {
+ fprintf(stderr, "unknown modop: %c\n", mod_op);
+ }
+ return cb.dbcb_resp_short(2, "modop");
+ }
+ }
+ lock_tables_if();
+ if (lock == 0) {
+ return cb.dbcb_resp_short(2, "lock_tables");
+ }
+ if (pst.get_table_id() >= table_vec.size()) {
+ return cb.dbcb_resp_short(2, "tblnum");
+ }
+ TABLE *const table = table_vec[pst.get_table_id()].table;
+ /* keys */
+ if (pst.get_idxnum() >= table->s->keys) {
+ return cb.dbcb_resp_short(2, "idxnum");
+ }
+ KEY& kinfo = table->key_info[pst.get_idxnum()];
+ if (args.kvalslen > kinfo.key_parts) {
+ return cb.dbcb_resp_short(2, "kpnum");
+ }
+ uchar key_buf[kinfo.key_length]; /* GNU */
+ size_t kplen_sum = 0;
+ {
+ DBG_KEY(fprintf(stderr, "SLOW\n"));
+ for (size_t i = 0; i < args.kvalslen; ++i) {
+ const KEY_PART_INFO & kpt = kinfo.key_part[i];
+ const string_ref& kval = args.kvals[i];
+ if (kval.begin() == 0) {
+ kpt.field->set_null();
+ } else {
+ kpt.field->set_notnull();
+ }
+ kpt.field->store(kval.begin(), kval.size(), &my_charset_bin);
+ kplen_sum += kpt.length;
+ }
+ key_copy(key_buf, table->record[0], &kinfo, kplen_sum);
+ }
+ /* filters */
+ uchar *filter_buf = 0;
+ if (args.filters != 0) {
+ const size_t filter_buf_len = calc_filter_buf_size(table, pst,
+ args.filters);
+ filter_buf = reinterpret_cast<uchar *>(alloca(filter_buf_len));
+ /* FIXME: TEST */
+ if (!fill_filter_buf(table, pst, args.filters, filter_buf,
+ filter_buf_len)) {
+ return cb.dbcb_resp_short(2, "filterblob");
+ }
+ }
+ /* handler */
+ table->read_set = &table->s->all_set;
+ handler *const hnd = table->file;
+ if (!for_write_flag) {
+ hnd->init_table_handle_for_HANDLER();
+ }
+ hnd->ha_index_or_rnd_end();
+ hnd->ha_index_init(pst.get_idxnum(), 1);
+ #if 0
+ statistic_increment(index_exec_count, &LOCK_status);
+ #endif
+ if (need_resp_record) {
+ cb.dbcb_resp_begin(pst.get_ret_fields().size());
+ }
+ const uint32_t limit = args.limit ? args.limit : 1;
+ uint32_t skip = args.skip;
+ size_t modified_count = 0;
+ int r = 0;
+ for (uint32_t i = 0; i < limit + skip; ++i) {
+ if (i == 0) {
+ const key_part_map kpm = (1U << args.kvalslen) - 1;
+ r = hnd->ha_index_read_map(table->record[0], key_buf, kpm, find_flag);
+ } else {
+ switch (find_flag) {
+ case HA_READ_BEFORE_KEY:
+ case HA_READ_KEY_OR_PREV:
+ r = hnd->ha_index_prev(table->record[0]);
+ break;
+ case HA_READ_AFTER_KEY:
+ case HA_READ_KEY_OR_NEXT:
+ r = hnd->ha_index_next(table->record[0]);
+ break;
+ case HA_READ_KEY_EXACT:
+ r = hnd->ha_index_next_same(table->record[0], key_buf, kplen_sum);
+ break;
+ default:
+ r = HA_ERR_END_OF_FILE; /* to finish the loop */
+ break;
+ }
+ }
+ if (debug_out) {
+ fprintf(stderr, "r=%d\n", r);
+ if (r == 0 || r == HA_ERR_RECORD_DELETED) {
+ dump_record(cb, table, pst);
+ }
+ }
+ int filter_res = 0;
+ if (r != 0) {
+ /* no-count */
+ } else if (args.filters != 0 && (filter_res = check_filter(cb, table,
+ pst, args.filters, filter_buf)) != 0) {
+ if (filter_res < 0) {
+ break;
+ }
+ } else if (skip > 0) {
+ --skip;
+ } else {
+ if (need_resp_record) {
+ resp_record(cb, table, pst);
+ }
+ if (mod_op != 0) {
+ r = modify_record(cb, table, pst, args, mod_op, modified_count);
+ }
+ }
+ if (r != 0 && r != HA_ERR_RECORD_DELETED) {
+ break;
+ }
+ }
+ hnd->ha_index_or_rnd_end();
+ if (r != 0 && r != HA_ERR_RECORD_DELETED && r != HA_ERR_KEY_NOT_FOUND &&
+ r != HA_ERR_END_OF_FILE) {
+ /* failed */
+ if (need_resp_record) {
+ /* revert dbcb_resp_begin() and dbcb_resp_entry() */
+ cb.dbcb_resp_cancel();
+ }
+ cb.dbcb_resp_short_num(2, r);
+ } else {
+ /* succeeded */
+ if (need_resp_record) {
+ cb.dbcb_resp_end();
+ } else {
+ cb.dbcb_resp_short_num(0, modified_count);
+ }
+ }
+}
+
+size_t
+dbcontext::calc_filter_buf_size(TABLE *table, const prep_stmt& pst,
+ const record_filter *filters)
+{
+ size_t filter_buf_len = 0;
+ for (const record_filter *f = filters; f->op.begin() != 0; ++f) {
+ if (f->val.begin() == 0) {
+ continue;
+ }
+ const uint32_t fn = pst.get_filter_fields()[f->ff_offset];
+ filter_buf_len += table->field[fn]->pack_length();
+ }
+ return filter_buf_len;
+}
+
+bool
+dbcontext::fill_filter_buf(TABLE *table, const prep_stmt& pst,
+ const record_filter *filters, uchar *filter_buf, size_t len)
+{
+ memset(filter_buf, 0, len);
+ size_t pos = 0;
+ for (const record_filter *f = filters; f->op.begin() != 0; ++f) {
+ if (f->val.begin() == 0) {
+ continue;
+ }
+ const uint32_t fn = pst.get_filter_fields()[f->ff_offset];
+ Field *const fld = table->field[fn];
+ if ((fld->flags & BLOB_FLAG) != 0) {
+ return false;
+ }
+ fld->store(f->val.begin(), f->val.size(), &my_charset_bin);
+ const size_t packlen = fld->pack_length();
+ memcpy(filter_buf + pos, fld->ptr, packlen);
+ pos += packlen;
+ }
+ return true;
+}
+
+int
+dbcontext::check_filter(dbcallback_i& cb, TABLE *table, const prep_stmt& pst,
+ const record_filter *filters, const uchar *filter_buf)
+{
+ DBG_FILTER(fprintf(stderr, "check_filter\n"));
+ size_t pos = 0;
+ for (const record_filter *f = filters; f->op.begin() != 0; ++f) {
+ const string_ref& op = f->op;
+ const string_ref& val = f->val;
+ const uint32_t fn = pst.get_filter_fields()[f->ff_offset];
+ Field *const fld = table->field[fn];
+ const size_t packlen = fld->pack_length();
+ const uchar *const bval = filter_buf + pos;
+ int cv = 0;
+ if (fld->is_null()) {
+ cv = (val.begin() == 0) ? 0 : -1;
+ } else {
+ cv = (val.begin() == 0) ? 1 : fld->cmp(bval);
+ }
+ DBG_FILTER(fprintf(stderr, "check_filter cv=%d\n", cv));
+ bool cond = true;
+ if (op.size() == 1) {
+ switch (op.begin()[0]) {
+ case '>':
+ DBG_FILTER(fprintf(stderr, "check_filter op: >\n"));
+ cond = (cv > 0);
+ break;
+ case '<':
+ DBG_FILTER(fprintf(stderr, "check_filter op: <\n"));
+ cond = (cv < 0);
+ break;
+ case '=':
+ DBG_FILTER(fprintf(stderr, "check_filter op: =\n"));
+ cond = (cv == 0);
+ break;
+ default:
+ DBG_FILTER(fprintf(stderr, "check_filter op: unknown\n"));
+ cond = false; /* FIXME: error */
+ break;
+ }
+ } else if (op.size() == 2 && op.begin()[1] == '=') {
+ switch (op.begin()[0]) {
+ case '>':
+ DBG_FILTER(fprintf(stderr, "check_filter op: >=\n"));
+ cond = (cv >= 0);
+ break;
+ case '<':
+ DBG_FILTER(fprintf(stderr, "check_filter op: <=\n"));
+ cond = (cv <= 0);
+ break;
+ case '!':
+ DBG_FILTER(fprintf(stderr, "check_filter op: !=\n"));
+ cond = (cv != 0);
+ break;
+ default:
+ DBG_FILTER(fprintf(stderr, "check_filter op: unknown\n"));
+ cond = false; /* FIXME: error */
+ break;
+ }
+ }
+ DBG_FILTER(fprintf(stderr, "check_filter cond: %d\n", (int)cond));
+ if (!cond) {
+ return (f->filter_type == record_filter_type_skip) ? 1 : -1;
+ }
+ if (val.begin() != 0) {
+ pos += packlen;
+ }
+ }
+ return 0;
+}
+
+void
+dbcontext::cmd_open_index(dbcallback_i& cb, size_t pst_id, const char *dbn,
+ const char *tbl, const char *idx, const char *retflds, const char *filflds)
+{
+ unlock_tables_if();
+ const table_name_type k = std::make_pair(std::string(dbn), std::string(tbl));
+ const table_map_type::const_iterator iter = table_map.find(k);
+ uint32_t tblnum = 0;
+ if (iter != table_map.end()) {
+ tblnum = iter->second;
+ DBG_CMP(fprintf(stderr, "HNDSOCK k=%s tblnum=%d\n", k.c_str(),
+ (int)tblnum));
+ } else {
+ TABLE_LIST tables;
+ TABLE *table = 0;
+ bool refresh = true;
+ const thr_lock_type lock_type = for_write_flag ? TL_WRITE : TL_READ;
+ #if MYSQL_VERSION_ID >= 50505
+ tables.init_one_table(dbn, strlen(dbn), tbl, strlen(tbl), tbl,
+ lock_type);
+ tables.mdl_request = mdl_request;
+ Open_table_context ot_act(thd, MYSQL_OPEN_REOPEN);
+ if (!open_table(thd, &tables, thd->mem_root, &ot_act)) {
+ table = tables.table;
+ }
+ #else
+ tables.init_one_table(dbn, tbl, lock_type);
+ table = open_table(thd, &tables, thd->mem_root, &refresh,
+ OPEN_VIEW_NO_PARSE);
+ #endif
+ if (table == 0) {
+ DENA_VERBOSE(10, fprintf(stderr,
+ "HNDSOCK failed to open %p [%s] [%s] [%d]\n",
+ thd, dbn, tbl, static_cast<int>(refresh)));
+ return cb.dbcb_resp_short(2, "open_table");
+ }
+ statistic_increment(open_tables_count, &LOCK_status);
+ table->reginfo.lock_type = lock_type;
+ table->use_all_columns();
+ tblnum = table_vec.size();
+ tablevec_entry e;
+ e.table = table;
+ table_vec.push_back(e);
+ table_map[k] = tblnum;
+ }
+ size_t idxnum = static_cast<size_t>(-1);
+ if (idx[0] >= '0' && idx[0] <= '9') {
+ /* numeric */
+ TABLE *const table = table_vec[tblnum].table;
+ idxnum = atoi(idx);
+ if (idxnum >= table->s->keys) {
+ return cb.dbcb_resp_short(2, "idxnum");
+ }
+ } else {
+ const char *const idx_name_to_open = idx[0] == '\0' ? "PRIMARY" : idx;
+ TABLE *const table = table_vec[tblnum].table;
+ for (uint i = 0; i < table->s->keys; ++i) {
+ KEY& kinfo = table->key_info[i];
+ if (strcmp(kinfo.name, idx_name_to_open) == 0) {
+ idxnum = i;
+ break;
+ }
+ }
+ }
+ if (idxnum == size_t(-1)) {
+ return cb.dbcb_resp_short(2, "idxnum");
+ }
+ prep_stmt::fields_type rf;
+ prep_stmt::fields_type ff;
+ if (!parse_fields(table_vec[tblnum].table, retflds, rf)) {
+ return cb.dbcb_resp_short(2, "fld");
+ }
+ if (!parse_fields(table_vec[tblnum].table, filflds, ff)) {
+ return cb.dbcb_resp_short(2, "fld");
+ }
+ prep_stmt p(this, tblnum, idxnum, rf, ff);
+ cb.dbcb_set_prep_stmt(pst_id, p);
+ return cb.dbcb_resp_short(0, "");
+}
+
+bool
+dbcontext::parse_fields(TABLE *const table, const char *str,
+ prep_stmt::fields_type& flds)
+{
+ string_ref flds_sr(str, strlen(str));
+ std::vector<string_ref> fldnms;
+ if (flds_sr.size() != 0) {
+ split(',', flds_sr, fldnms);
+ }
+ for (size_t i = 0; i < fldnms.size(); ++i) {
+ Field **fld = 0;
+ size_t j = 0;
+ for (fld = table->field; *fld; ++fld, ++j) {
+ DBG_FLD(fprintf(stderr, "f %s\n", (*fld)->field_name));
+ string_ref fn((*fld)->field_name, strlen((*fld)->field_name));
+ if (fn == fldnms[i]) {
+ break;
+ }
+ }
+ if (*fld == 0) {
+ DBG_FLD(fprintf(stderr, "UNKNOWN FLD %s [%s]\n", retflds,
+ std::string(fldnms[i].begin(), fldnms[i].size()).c_str()));
+ return false;
+ }
+ DBG_FLD(fprintf(stderr, "FLD %s %zu\n", (*fld)->field_name, j));
+ flds.push_back(j);
+ }
+ return true;
+}
+
+enum db_write_op {
+ db_write_op_none = 0,
+ db_write_op_insert = 1,
+ db_write_op_sql = 2,
+};
+
+void
+dbcontext::cmd_exec_on_index(dbcallback_i& cb, const cmd_exec_args& args)
+{
+ const prep_stmt& p = *args.pst;
+ if (p.get_table_id() == static_cast<size_t>(-1)) {
+ return cb.dbcb_resp_short(2, "stmtnum");
+ }
+ ha_rkey_function find_flag = HA_READ_KEY_EXACT;
+ db_write_op wrop = db_write_op_none;
+ if (args.op.size() == 1) {
+ switch (args.op.begin()[0]) {
+ case '=':
+ find_flag = HA_READ_KEY_EXACT;
+ break;
+ case '>':
+ find_flag = HA_READ_AFTER_KEY;
+ break;
+ case '<':
+ find_flag = HA_READ_BEFORE_KEY;
+ break;
+ case '+':
+ wrop = db_write_op_insert;
+ break;
+ case 'S':
+ wrop = db_write_op_sql;
+ break;
+ default:
+ return cb.dbcb_resp_short(1, "op");
+ }
+ } else if (args.op.size() == 2 && args.op.begin()[1] == '=') {
+ switch (args.op.begin()[0]) {
+ case '>':
+ find_flag = HA_READ_KEY_OR_NEXT;
+ break;
+ case '<':
+ find_flag = HA_READ_KEY_OR_PREV;
+ break;
+ default:
+ return cb.dbcb_resp_short(1, "op");
+ }
+ } else {
+ return cb.dbcb_resp_short(1, "op");
+ }
+ if (args.kvalslen <= 0) {
+ return cb.dbcb_resp_short(2, "klen");
+ }
+ switch (wrop) {
+ case db_write_op_none:
+ return cmd_find_internal(cb, p, find_flag, args);
+ case db_write_op_insert:
+ return cmd_insert_internal(cb, p, args.kvals, args.kvalslen);
+ case db_write_op_sql:
+ return cmd_sql_internal(cb, p, args.kvals, args.kvalslen);
+ }
+}
+
+void
+dbcontext::set_statistics(size_t num_conns, size_t num_active)
+{
+ thd_proc_info(thd, &info_message_buf[0]);
+ if (for_write_flag) {
+ set_thread_message("handlersocket: mode=wr, %zu conns, %zu active",
+ num_conns, num_active);
+ } else {
+ set_thread_message("handlersocket: mode=rd, %zu conns, %zu active",
+ num_conns, num_active);
+ }
+}
+
+};
+
diff --git a/plugin/handler_socket/handlersocket/database.hpp b/plugin/handler_socket/handlersocket/database.hpp
new file mode 100644
index 00000000000..5fc3fe737b5
--- /dev/null
+++ b/plugin/handler_socket/handlersocket/database.hpp
@@ -0,0 +1,130 @@
+
+// vim:sw=2:ai
+
+/*
+ * Copyright (C) 2010 DeNA Co.,Ltd.. All rights reserved.
+ * See COPYRIGHT.txt for details.
+ */
+
+#ifndef DENA_DATABASE_HPP
+#define DENA_DATABASE_HPP
+
+#include <string>
+#include <memory>
+#include <vector>
+#include <stdint.h>
+
+#include "string_buffer.hpp"
+#include "string_ref.hpp"
+#include "config.hpp"
+
+namespace dena {
+
+struct database_i;
+typedef std::auto_ptr<volatile database_i> database_ptr;
+
+struct dbcontext_i;
+typedef std::auto_ptr<dbcontext_i> dbcontext_ptr;
+
+struct database_i {
+ virtual ~database_i() { }
+ virtual dbcontext_ptr create_context(bool for_write) volatile = 0;
+ virtual void stop() volatile = 0;
+ virtual const config& get_conf() const volatile = 0;
+ static database_ptr create(const config& conf);
+};
+
+struct prep_stmt {
+ typedef std::vector<uint32_t> fields_type;
+ private:
+ dbcontext_i *dbctx; /* must be valid while *this is alive */
+ size_t table_id; /* a prep_stmt object holds a refcount of the table */
+ size_t idxnum;
+ fields_type ret_fields;
+ fields_type filter_fields;
+ public:
+ prep_stmt();
+ prep_stmt(dbcontext_i *c, size_t tbl, size_t idx, const fields_type& rf,
+ const fields_type& ff);
+ ~prep_stmt();
+ prep_stmt(const prep_stmt& x);
+ prep_stmt& operator =(const prep_stmt& x);
+ public:
+ size_t get_table_id() const { return table_id; }
+ size_t get_idxnum() const { return idxnum; }
+ const fields_type& get_ret_fields() const { return ret_fields; }
+ const fields_type& get_filter_fields() const { return filter_fields; }
+};
+
+struct dbcallback_i {
+ virtual ~dbcallback_i () { }
+ virtual void dbcb_set_prep_stmt(size_t pst_id, const prep_stmt& v) = 0;
+ virtual const prep_stmt *dbcb_get_prep_stmt(size_t pst_id) const = 0;
+ virtual void dbcb_resp_short(uint32_t code, const char *msg) = 0;
+ virtual void dbcb_resp_short_num(uint32_t code, uint32_t value) = 0;
+ virtual void dbcb_resp_begin(size_t num_flds) = 0;
+ virtual void dbcb_resp_entry(const char *fld, size_t fldlen) = 0;
+ virtual void dbcb_resp_end() = 0;
+ virtual void dbcb_resp_cancel() = 0;
+};
+
+enum record_filter_type {
+ record_filter_type_skip = 0,
+ record_filter_type_break = 1,
+};
+
+struct record_filter {
+ record_filter_type filter_type;
+ string_ref op;
+ uint32_t ff_offset; /* offset in filter_fields */
+ string_ref val;
+ record_filter() : filter_type(record_filter_type_skip), ff_offset(0) { }
+};
+
+struct cmd_exec_args {
+ const prep_stmt *pst;
+ string_ref op;
+ const string_ref *kvals;
+ size_t kvalslen;
+ uint32_t limit;
+ uint32_t skip;
+ string_ref mod_op;
+ const string_ref *uvals; /* size must be pst->retfieelds.size() */
+ const record_filter *filters;
+ cmd_exec_args() : pst(0), kvals(0), kvalslen(0), limit(0), skip(0),
+ uvals(0), filters(0) { }
+};
+
+struct dbcontext_i {
+ virtual ~dbcontext_i() { }
+ virtual void init_thread(const void *stack_bottom,
+ volatile int& shutdown_flag) = 0;
+ virtual void term_thread() = 0;
+ virtual bool check_alive() = 0;
+ virtual void lock_tables_if() = 0;
+ virtual void unlock_tables_if() = 0;
+ virtual bool get_commit_error() = 0;
+ virtual void clear_error() = 0;
+ virtual void close_tables_if() = 0;
+ virtual void table_addref(size_t tbl_id) = 0; /* TODO: hide */
+ virtual void table_release(size_t tbl_id) = 0; /* TODO: hide */
+ virtual void cmd_open_index(dbcallback_i& cb, size_t pst_id, const char *dbn,
+ const char *tbl, const char *idx, const char *retflds,
+ const char *filflds) = 0;
+ virtual void cmd_exec_on_index(dbcallback_i& cb, const cmd_exec_args& args)
+ = 0;
+ virtual void set_statistics(size_t num_conns, size_t num_active) = 0;
+};
+
+};
+
+extern unsigned long long int open_tables_count;
+extern unsigned long long int close_tables_count;
+extern unsigned long long int lock_tables_count;
+extern unsigned long long int unlock_tables_count;
+#if 0
+extern unsigned long long int index_exec_count;
+#endif
+
+#endif
+
diff --git a/plugin/handler_socket/handlersocket/handlersocket.cpp b/plugin/handler_socket/handlersocket/handlersocket.cpp
new file mode 100644
index 00000000000..7b60eaa28d3
--- /dev/null
+++ b/plugin/handler_socket/handlersocket/handlersocket.cpp
@@ -0,0 +1,216 @@
+
+// vim:sw=2:ai
+
+/*
+ * Copyright (C) 2010 DeNA Co.,Ltd.. All rights reserved.
+ * See COPYRIGHT.txt for details.
+ */
+
+#include <memory>
+#include <string>
+#include <stdio.h>
+
+#include "config.hpp"
+#include "hstcpsvr.hpp"
+#include "string_util.hpp"
+#include "mysql_incl.hpp"
+
+#define DBG_LOG \
+ if (dena::verbose_level >= 100) { \
+ fprintf(stderr, "%s %p\n", __PRETTY_FUNCTION__, this); \
+ }
+#define DBG_DO(x) if (dena::verbose_level >= 100) { x; }
+
+#define DBG_DIR(x)
+
+using namespace dena;
+
+static char *handlersocket_address = 0;
+static char *handlersocket_port = 0;
+static char *handlersocket_port_wr = 0;
+static unsigned int handlersocket_epoll = 1;
+static unsigned int handlersocket_threads = 32;
+static unsigned int handlersocket_threads_wr = 1;
+static unsigned int handlersocket_timeout = 30;
+static unsigned int handlersocket_backlog = 32768;
+static unsigned int handlersocket_sndbuf = 0;
+static unsigned int handlersocket_rcvbuf = 0;
+static unsigned int handlersocket_readsize = 0;
+static unsigned int handlersocket_accept_balance = 0;
+static unsigned int handlersocket_wrlock_timeout = 0;
+static char *handlersocket_plain_secret = 0;
+static char *handlersocket_plain_secret_wr = 0;
+
+struct daemon_handlersocket_data {
+ hstcpsvr_ptr hssvr_rd;
+ hstcpsvr_ptr hssvr_wr;
+};
+
+static int
+daemon_handlersocket_init(void *p)
+{
+ DENA_VERBOSE(10, fprintf(stderr, "handlersocket: initialized\n"));
+ config conf;
+ conf["use_epoll"] = handlersocket_epoll ? "1" : "0";
+ if (handlersocket_address) {
+ conf["host"] = handlersocket_address;
+ }
+ if (handlersocket_port) {
+ conf["port"] = handlersocket_port;
+ }
+ /*
+ * unix domain socket
+ * conf["host"] = "/";
+ * conf["port"] = "/tmp/handlersocket";
+ */
+ if (handlersocket_threads > 0) {
+ conf["num_threads"] = to_stdstring(handlersocket_threads);
+ } else {
+ conf["num_threads"] = "1";
+ }
+ conf["timeout"] = to_stdstring(handlersocket_timeout);
+ conf["listen_backlog"] = to_stdstring(handlersocket_backlog);
+ conf["sndbuf"] = to_stdstring(handlersocket_sndbuf);
+ conf["rcvbuf"] = to_stdstring(handlersocket_rcvbuf);
+ conf["readsize"] = to_stdstring(handlersocket_readsize);
+ conf["accept_balance"] = to_stdstring(handlersocket_accept_balance);
+ conf["wrlock_timeout"] = to_stdstring(handlersocket_wrlock_timeout);
+ std::auto_ptr<daemon_handlersocket_data> ap(new daemon_handlersocket_data);
+ if (handlersocket_port != 0 && handlersocket_port_wr != handlersocket_port) {
+ conf["port"] = handlersocket_port;
+ if (handlersocket_plain_secret) {
+ conf["plain_secret"] = handlersocket_plain_secret;
+ }
+ ap->hssvr_rd = hstcpsvr_i::create(conf);
+ ap->hssvr_rd->start_listen();
+ }
+ if (handlersocket_port_wr != 0) {
+ if (handlersocket_threads_wr > 0) {
+ conf["num_threads"] = to_stdstring(handlersocket_threads_wr);
+ }
+ conf["port"] = handlersocket_port_wr;
+ conf["for_write"] = "1";
+ conf["plain_secret"] = "";
+ if (handlersocket_plain_secret_wr) {
+ conf["plain_secret"] = handlersocket_plain_secret_wr;
+ }
+ ap->hssvr_wr = hstcpsvr_i::create(conf);
+ ap->hssvr_wr->start_listen();
+ }
+ st_plugin_int *const plugin = static_cast<st_plugin_int *>(p);
+ plugin->data = ap.release();
+ return 0;
+}
+
+static int
+daemon_handlersocket_deinit(void *p)
+{
+ DENA_VERBOSE(10, fprintf(stderr, "handlersocket: terminated\n"));
+ st_plugin_int *const plugin = static_cast<st_plugin_int *>(p);
+ daemon_handlersocket_data *ptr =
+ static_cast<daemon_handlersocket_data *>(plugin->data);
+ delete ptr;
+ return 0;
+}
+
+static struct st_mysql_daemon daemon_handlersocket_plugin = {
+ MYSQL_DAEMON_INTERFACE_VERSION
+};
+
+static MYSQL_SYSVAR_UINT(verbose, dena::verbose_level, 0,
+ "0..10000", 0, 0, 10 /* default */, 0, 10000, 0);
+static MYSQL_SYSVAR_UINT(epoll, handlersocket_epoll, PLUGIN_VAR_READONLY,
+ "0..1", 0, 0, 1 /* default */, 0, 1, 0);
+static MYSQL_SYSVAR_STR(address, handlersocket_address,
+ PLUGIN_VAR_READONLY | PLUGIN_VAR_MEMALLOC, "", NULL, NULL, NULL);
+static MYSQL_SYSVAR_STR(port, handlersocket_port,
+ PLUGIN_VAR_READONLY | PLUGIN_VAR_MEMALLOC, "", NULL, NULL, NULL);
+static MYSQL_SYSVAR_STR(port_wr, handlersocket_port_wr,
+ PLUGIN_VAR_READONLY | PLUGIN_VAR_MEMALLOC, "", NULL, NULL, NULL);
+static MYSQL_SYSVAR_UINT(threads, handlersocket_threads, PLUGIN_VAR_READONLY,
+ "1..3000", 0, 0, 16 /* default */, 1, 3000, 0);
+static MYSQL_SYSVAR_UINT(threads_wr, handlersocket_threads_wr,
+ PLUGIN_VAR_READONLY, "1..3000", 0, 0, 1 /* default */, 1, 3000, 0);
+static MYSQL_SYSVAR_UINT(timeout, handlersocket_timeout, PLUGIN_VAR_READONLY,
+ "30..3600", 0, 0, 300 /* default */, 30, 3600, 0);
+static MYSQL_SYSVAR_UINT(backlog, handlersocket_backlog, PLUGIN_VAR_READONLY,
+ "5..1000000", 0, 0, 32768 /* default */, 5, 1000000, 0);
+static MYSQL_SYSVAR_UINT(sndbuf, handlersocket_sndbuf, PLUGIN_VAR_READONLY,
+ "0..16777216", 0, 0, 0 /* default */, 0, 16777216, 0);
+static MYSQL_SYSVAR_UINT(rcvbuf, handlersocket_rcvbuf, PLUGIN_VAR_READONLY,
+ "0..16777216", 0, 0, 0 /* default */, 0, 16777216, 0);
+static MYSQL_SYSVAR_UINT(readsize, handlersocket_readsize, PLUGIN_VAR_READONLY,
+ "0..16777216", 0, 0, 0 /* default */, 0, 16777216, 0);
+static MYSQL_SYSVAR_UINT(accept_balance, handlersocket_accept_balance,
+ PLUGIN_VAR_READONLY, "0..10000", 0, 0, 0 /* default */, 0, 10000, 0);
+static MYSQL_SYSVAR_UINT(wrlock_timeout, handlersocket_wrlock_timeout,
+ PLUGIN_VAR_READONLY, "0..3600", 0, 0, 12 /* default */, 0, 3600, 0);
+static MYSQL_SYSVAR_STR(plain_secret, handlersocket_plain_secret,
+ PLUGIN_VAR_READONLY | PLUGIN_VAR_MEMALLOC, "", NULL, NULL, NULL);
+static MYSQL_SYSVAR_STR(plain_secret_wr, handlersocket_plain_secret_wr,
+ PLUGIN_VAR_READONLY | PLUGIN_VAR_MEMALLOC, "", NULL, NULL, NULL);
+
+
+/* warning: type-punning to incomplete type might break strict-aliasing
+ * rules */
+static struct st_mysql_sys_var *daemon_handlersocket_system_variables[] = {
+ MYSQL_SYSVAR(verbose),
+ MYSQL_SYSVAR(address),
+ MYSQL_SYSVAR(port),
+ MYSQL_SYSVAR(port_wr),
+ MYSQL_SYSVAR(epoll),
+ MYSQL_SYSVAR(threads),
+ MYSQL_SYSVAR(threads_wr),
+ MYSQL_SYSVAR(timeout),
+ MYSQL_SYSVAR(backlog),
+ MYSQL_SYSVAR(sndbuf),
+ MYSQL_SYSVAR(rcvbuf),
+ MYSQL_SYSVAR(readsize),
+ MYSQL_SYSVAR(accept_balance),
+ MYSQL_SYSVAR(wrlock_timeout),
+ MYSQL_SYSVAR(plain_secret),
+ MYSQL_SYSVAR(plain_secret_wr),
+ 0
+};
+
+static SHOW_VAR hs_status_variables[] = {
+ {"table_open", (char*) &open_tables_count, SHOW_LONGLONG},
+ {"table_close", (char*) &close_tables_count, SHOW_LONGLONG},
+ {"table_lock", (char*) &lock_tables_count, SHOW_LONGLONG},
+ {"table_unlock", (char*) &unlock_tables_count, SHOW_LONGLONG},
+ #if 0
+ {"index_exec", (char*) &index_exec_count, SHOW_LONGLONG},
+ #endif
+ {NullS, NullS, SHOW_LONG}
+};
+
+static int show_hs_vars(THD *thd, SHOW_VAR *var, char *buff)
+{
+ var->type= SHOW_ARRAY;
+ var->value= (char *) &hs_status_variables;
+ return 0;
+}
+
+static SHOW_VAR daemon_handlersocket_status_variables[] = {
+ {"Hs", (char*) show_hs_vars, SHOW_FUNC},
+ {NullS, NullS, SHOW_LONG}
+};
+
+
+mysql_declare_plugin(handlersocket)
+{
+ MYSQL_DAEMON_PLUGIN,
+ &daemon_handlersocket_plugin,
+ "handlersocket",
+ "higuchi dot akira at dena dot jp",
+ "",
+ PLUGIN_LICENSE_BSD,
+ daemon_handlersocket_init,
+ daemon_handlersocket_deinit,
+ 0x0100 /* 1.0 */,
+ daemon_handlersocket_status_variables,
+ daemon_handlersocket_system_variables,
+ 0
+}
+mysql_declare_plugin_end;
+
diff --git a/plugin/handler_socket/handlersocket/handlersocket.spec.template b/plugin/handler_socket/handlersocket/handlersocket.spec.template
new file mode 100644
index 00000000000..0ce8c0cb382
--- /dev/null
+++ b/plugin/handler_socket/handlersocket/handlersocket.spec.template
@@ -0,0 +1,29 @@
+Summary: handlersocket plugin for mysql
+Name: handlersocket
+Version: HANDLERSOCKET_VERSION
+Release: 1%{?dist}
+Group: System Environment/Libraries
+License: BSD
+Source: handlersocket.tar.gz
+Packager: Akira Higuchi <higuchi dot akira at dena dot jp>
+BuildRoot: /var/tmp/%{name}-%{version}-root
+
+%description
+
+%prep
+%setup -n %{name}
+
+%define _use_internal_dependency_generator 0
+
+%build
+make -f Makefile.plain
+
+%install
+rm -rf $RPM_BUILD_ROOT
+mkdir -p $RPM_BUILD_ROOT/%{_libdir}/mysql/plugin
+install -m 755 handlersocket.so $RPM_BUILD_ROOT/%{_libdir}/mysql/plugin/
+
+%files
+%defattr(-, root, root)
+%{_libdir}/mysql/plugin/*.so
+
diff --git a/plugin/handler_socket/handlersocket/hstcpsvr.cpp b/plugin/handler_socket/handlersocket/hstcpsvr.cpp
new file mode 100644
index 00000000000..13df7ba0838
--- /dev/null
+++ b/plugin/handler_socket/handlersocket/hstcpsvr.cpp
@@ -0,0 +1,149 @@
+
+// vim:sw=2:ai
+
+/*
+ * Copyright (C) 2010 DeNA Co.,Ltd.. All rights reserved.
+ * See COPYRIGHT.txt for details.
+ */
+
+#include <stdlib.h>
+#include <vector>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/resource.h>
+
+#include "hstcpsvr.hpp"
+#include "hstcpsvr_worker.hpp"
+#include "thread.hpp"
+#include "fatal.hpp"
+#include "auto_ptrcontainer.hpp"
+
+#define DBG(x)
+
+namespace dena {
+
+struct worker_throbj {
+ worker_throbj(const hstcpsvr_worker_arg& arg)
+ : worker(hstcpsvr_worker_i::create(arg)) { }
+ void operator ()() {
+ worker->run();
+ }
+ hstcpsvr_worker_ptr worker;
+};
+
+struct hstcpsvr : public hstcpsvr_i, private noncopyable {
+ hstcpsvr(const config& c);
+ ~hstcpsvr();
+ virtual std::string start_listen();
+ private:
+ hstcpsvr_shared_c cshared;
+ volatile hstcpsvr_shared_v vshared;
+ typedef thread<worker_throbj> worker_thread_type;
+ typedef auto_ptrcontainer< std::vector<worker_thread_type *> > threads_type;
+ threads_type threads;
+ std::vector<unsigned int> thread_num_conns_vec;
+ private:
+ void stop_workers();
+};
+
+namespace {
+
+void
+check_nfile(size_t nfile)
+{
+ struct rlimit rl;
+ const int r = getrlimit(RLIMIT_NOFILE, &rl);
+ if (r != 0) {
+ fatal_abort("check_nfile: getrlimit failed");
+ }
+ if (rl.rlim_cur < static_cast<rlim_t>(nfile + 1000)) {
+ fprintf(stderr,
+ "[Warning] handlersocket: open_files_limit is too small.\n");
+ }
+}
+
+};
+
+hstcpsvr::hstcpsvr(const config& c)
+ : cshared(), vshared()
+{
+ vshared.shutdown = 0;
+ cshared.conf = c; /* copy */
+ if (cshared.conf["port"] == "") {
+ cshared.conf["port"] = "9999";
+ }
+ cshared.num_threads = cshared.conf.get_int("num_threads", 32);
+ cshared.sockargs.nonblocking = cshared.conf.get_int("nonblocking", 1);
+ cshared.sockargs.use_epoll = cshared.conf.get_int("use_epoll", 1);
+ if (cshared.sockargs.use_epoll) {
+ cshared.sockargs.nonblocking = 1;
+ }
+ cshared.readsize = cshared.conf.get_int("readsize", 1);
+ cshared.nb_conn_per_thread = cshared.conf.get_int("conn_per_thread", 1024);
+ cshared.for_write_flag = cshared.conf.get_int("for_write", 0);
+ cshared.plain_secret = cshared.conf.get_str("plain_secret", "");
+ cshared.require_auth = !cshared.plain_secret.empty();
+ cshared.sockargs.set(cshared.conf);
+ cshared.dbptr = database_i::create(c);
+ check_nfile(cshared.num_threads * cshared.nb_conn_per_thread);
+ thread_num_conns_vec.resize(cshared.num_threads);
+ cshared.thread_num_conns = thread_num_conns_vec.empty()
+ ? 0 : &thread_num_conns_vec[0];
+}
+
+hstcpsvr::~hstcpsvr()
+{
+ stop_workers();
+}
+
+std::string
+hstcpsvr::start_listen()
+{
+ std::string err;
+ if (threads.size() != 0) {
+ return "start_listen: already running";
+ }
+ if (socket_bind(cshared.listen_fd, cshared.sockargs, err) != 0) {
+ return "bind: " + err;
+ }
+ DENA_VERBOSE(20, fprintf(stderr, "bind done\n"));
+ const size_t stack_size = std::max(
+ cshared.conf.get_int("stack_size", 1 * 1024LL * 1024), 8 * 1024LL * 1024);
+ for (long i = 0; i < cshared.num_threads; ++i) {
+ hstcpsvr_worker_arg arg;
+ arg.cshared = &cshared;
+ arg.vshared = &vshared;
+ arg.worker_id = i;
+ std::auto_ptr< thread<worker_throbj> > thr(
+ new thread<worker_throbj>(arg, stack_size));
+ threads.push_back_ptr(thr);
+ }
+ DENA_VERBOSE(20, fprintf(stderr, "threads created\n"));
+ for (size_t i = 0; i < threads.size(); ++i) {
+ threads[i]->start();
+ }
+ DENA_VERBOSE(20, fprintf(stderr, "threads started\n"));
+ return std::string();
+}
+
+void
+hstcpsvr::stop_workers()
+{
+ vshared.shutdown = 1;
+ for (size_t i = 0; i < threads.size(); ++i) {
+ threads[i]->join();
+ }
+ threads.clear();
+}
+
+hstcpsvr_ptr
+hstcpsvr_i::create(const config& conf)
+{
+ return hstcpsvr_ptr(new hstcpsvr(conf));
+}
+
+};
+
diff --git a/plugin/handler_socket/handlersocket/hstcpsvr.hpp b/plugin/handler_socket/handlersocket/hstcpsvr.hpp
new file mode 100644
index 00000000000..811bfa25613
--- /dev/null
+++ b/plugin/handler_socket/handlersocket/hstcpsvr.hpp
@@ -0,0 +1,58 @@
+
+// vim:sw=2:ai
+
+/*
+ * Copyright (C) 2010 DeNA Co.,Ltd.. All rights reserved.
+ * See COPYRIGHT.txt for details.
+ */
+
+#ifndef DENA_HSTCPSVR_HPP
+#define DENA_HSTCPSVR_HPP
+
+#include <memory>
+#include <string>
+#include <map>
+
+#include "mutex.hpp"
+#include "auto_file.hpp"
+#include "database.hpp"
+#include "config.hpp"
+#include "socket.hpp"
+
+namespace dena {
+
+struct hstcpsvr_shared_c {
+ config conf;
+ long num_threads;
+ long nb_conn_per_thread;
+ bool for_write_flag;
+ bool require_auth;
+ std::string plain_secret;
+ int readsize;
+ socket_args sockargs;
+ auto_file listen_fd;
+ database_ptr dbptr;
+ volatile unsigned int *thread_num_conns; /* 0 .. num_threads-1 */
+ hstcpsvr_shared_c() : num_threads(0), nb_conn_per_thread(100),
+ for_write_flag(false), require_auth(false), readsize(0),
+ thread_num_conns(0) { }
+};
+
+struct hstcpsvr_shared_v : public mutex {
+ int shutdown;
+ hstcpsvr_shared_v() : shutdown(0) { }
+};
+
+struct hstcpsvr_i;
+typedef std::auto_ptr<hstcpsvr_i> hstcpsvr_ptr;
+
+struct hstcpsvr_i {
+ virtual ~hstcpsvr_i() { }
+ virtual std::string start_listen() = 0;
+ static hstcpsvr_ptr create(const config& conf);
+};
+
+};
+
+#endif
+
diff --git a/plugin/handler_socket/handlersocket/hstcpsvr_worker.cpp b/plugin/handler_socket/handlersocket/hstcpsvr_worker.cpp
new file mode 100644
index 00000000000..ac01b611aa6
--- /dev/null
+++ b/plugin/handler_socket/handlersocket/hstcpsvr_worker.cpp
@@ -0,0 +1,896 @@
+
+// vim:sw=2:ai
+
+/*
+ * Copyright (C) 2010 DeNA Co.,Ltd.. All rights reserved.
+ * See COPYRIGHT.txt for details.
+ */
+
+#include <netinet/in.h>
+#include <errno.h>
+#include <poll.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdexcept>
+#include <signal.h>
+#include <list>
+#if __linux__
+#include <sys/epoll.h>
+#endif
+
+#include "hstcpsvr_worker.hpp"
+#include "string_buffer.hpp"
+#include "auto_ptrcontainer.hpp"
+#include "string_util.hpp"
+#include "escape.hpp"
+
+#define DBG_FD(x)
+#define DBG_TR(x)
+#define DBG_EP(x)
+
+/* TODO */
+#if !defined(__linux__) && !defined(__FreeBSD__) && !defined(MSG_NOSIGNAL)
+#define MSG_NOSIGNAL 0
+#endif
+
+namespace dena {
+
+struct dbconnstate {
+ string_buffer readbuf;
+ string_buffer writebuf;
+ std::vector<prep_stmt> prep_stmts;
+ size_t resp_begin_pos;
+ void reset() {
+ readbuf.clear();
+ writebuf.clear();
+ prep_stmts.clear();
+ resp_begin_pos = 0;
+ }
+ dbconnstate() : resp_begin_pos(0) { }
+};
+
+struct hstcpsvr_conn;
+typedef auto_ptrcontainer< std::list<hstcpsvr_conn *> > hstcpsvr_conns_type;
+
+struct hstcpsvr_conn : public dbcallback_i {
+ public:
+ auto_file fd;
+ sockaddr_storage addr;
+ socklen_t addr_len;
+ dbconnstate cstate;
+ std::string err;
+ size_t readsize;
+ bool nonblocking;
+ bool read_finished;
+ bool write_finished;
+ time_t nb_last_io;
+ hstcpsvr_conns_type::iterator conns_iter;
+ bool authorized;
+ public:
+ bool closed() const;
+ bool ok_to_close() const;
+ void reset();
+ int accept(const hstcpsvr_shared_c& cshared);
+ bool write_more(bool *more_r = 0);
+ bool read_more(bool *more_r = 0);
+ public:
+ virtual void dbcb_set_prep_stmt(size_t pst_id, const prep_stmt& v);
+ virtual const prep_stmt *dbcb_get_prep_stmt(size_t pst_id) const;
+ virtual void dbcb_resp_short(uint32_t code, const char *msg);
+ virtual void dbcb_resp_short_num(uint32_t code, uint32_t value);
+ virtual void dbcb_resp_begin(size_t num_flds);
+ virtual void dbcb_resp_entry(const char *fld, size_t fldlen);
+ virtual void dbcb_resp_end();
+ virtual void dbcb_resp_cancel();
+ public:
+ hstcpsvr_conn() : addr_len(sizeof(addr)), readsize(4096),
+ nonblocking(false), read_finished(false), write_finished(false),
+ nb_last_io(0), authorized(false) { }
+};
+
+bool
+hstcpsvr_conn::closed() const
+{
+ return fd.get() < 0;
+}
+
+bool
+hstcpsvr_conn::ok_to_close() const
+{
+ return write_finished || (read_finished && cstate.writebuf.size() == 0);
+}
+
+void
+hstcpsvr_conn::reset()
+{
+ addr = sockaddr_storage();
+ addr_len = sizeof(addr);
+ cstate.reset();
+ fd.reset();
+ read_finished = false;
+ write_finished = false;
+}
+
+int
+hstcpsvr_conn::accept(const hstcpsvr_shared_c& cshared)
+{
+ reset();
+ return socket_accept(cshared.listen_fd.get(), fd, cshared.sockargs, addr,
+ addr_len, err);
+}
+
+bool
+hstcpsvr_conn::write_more(bool *more_r)
+{
+ if (write_finished || cstate.writebuf.size() == 0) {
+ return false;
+ }
+ const size_t wlen = cstate.writebuf.size();
+ ssize_t len = send(fd.get(), cstate.writebuf.begin(), wlen, MSG_NOSIGNAL);
+ if (len <= 0) {
+ if (len == 0 || !nonblocking || errno != EWOULDBLOCK) {
+ cstate.writebuf.clear();
+ write_finished = true;
+ }
+ return false;
+ }
+ cstate.writebuf.erase_front(len);
+ /* FIXME: reallocate memory if too large */
+ if (more_r) {
+ *more_r = (static_cast<size_t>(len) == wlen);
+ }
+ return true;
+}
+
+bool
+hstcpsvr_conn::read_more(bool *more_r)
+{
+ if (read_finished) {
+ return false;
+ }
+ const size_t block_size = readsize > 4096 ? readsize : 4096;
+ char *wp = cstate.readbuf.make_space(block_size);
+ const ssize_t len = read(fd.get(), wp, block_size);
+ if (len <= 0) {
+ if (len == 0 || !nonblocking || errno != EWOULDBLOCK) {
+ read_finished = true;
+ }
+ return false;
+ }
+ cstate.readbuf.space_wrote(len);
+ if (more_r) {
+ *more_r = (static_cast<size_t>(len) == block_size);
+ }
+ return true;
+}
+
+void
+hstcpsvr_conn::dbcb_set_prep_stmt(size_t pst_id, const prep_stmt& v)
+{
+ if (cstate.prep_stmts.size() <= pst_id) {
+ cstate.prep_stmts.resize(pst_id + 1);
+ }
+ cstate.prep_stmts[pst_id] = v;
+}
+
+const prep_stmt *
+hstcpsvr_conn::dbcb_get_prep_stmt(size_t pst_id) const
+{
+ if (cstate.prep_stmts.size() <= pst_id) {
+ return 0;
+ }
+ return &cstate.prep_stmts[pst_id];
+}
+
+void
+hstcpsvr_conn::dbcb_resp_short(uint32_t code, const char *msg)
+{
+ write_ui32(cstate.writebuf, code);
+ const size_t msglen = strlen(msg);
+ if (msglen != 0) {
+ cstate.writebuf.append_literal("\t1\t");
+ cstate.writebuf.append(msg, msg + msglen);
+ } else {
+ cstate.writebuf.append_literal("\t1");
+ }
+ cstate.writebuf.append_literal("\n");
+}
+
+void
+hstcpsvr_conn::dbcb_resp_short_num(uint32_t code, uint32_t value)
+{
+ write_ui32(cstate.writebuf, code);
+ cstate.writebuf.append_literal("\t1\t");
+ write_ui32(cstate.writebuf, value);
+ cstate.writebuf.append_literal("\n");
+}
+
+void
+hstcpsvr_conn::dbcb_resp_begin(size_t num_flds)
+{
+ cstate.resp_begin_pos = cstate.writebuf.size();
+ cstate.writebuf.append_literal("0\t");
+ write_ui32(cstate.writebuf, num_flds);
+}
+
+void
+hstcpsvr_conn::dbcb_resp_entry(const char *fld, size_t fldlen)
+{
+ if (fld != 0) {
+ cstate.writebuf.append_literal("\t");
+ escape_string(cstate.writebuf, fld, fld + fldlen);
+ } else {
+ static const char t[] = "\t\0";
+ cstate.writebuf.append(t, t + 2);
+ }
+}
+
+void
+hstcpsvr_conn::dbcb_resp_end()
+{
+ cstate.writebuf.append_literal("\n");
+ cstate.resp_begin_pos = 0;
+}
+
+void
+hstcpsvr_conn::dbcb_resp_cancel()
+{
+ cstate.writebuf.resize(cstate.resp_begin_pos);
+ cstate.resp_begin_pos = 0;
+}
+
+struct hstcpsvr_worker : public hstcpsvr_worker_i, private noncopyable {
+ hstcpsvr_worker(const hstcpsvr_worker_arg& arg);
+ virtual void run();
+ private:
+ const hstcpsvr_shared_c& cshared;
+ volatile hstcpsvr_shared_v& vshared;
+ long worker_id;
+ dbcontext_ptr dbctx;
+ hstcpsvr_conns_type conns; /* conns refs dbctx */
+ time_t last_check_time;
+ std::vector<pollfd> pfds;
+ #ifdef __linux__
+ std::vector<epoll_event> events_vec;
+ auto_file epoll_fd;
+ #endif
+ bool accept_enabled;
+ int accept_balance;
+ std::vector<record_filter> filters_work;
+ private:
+ int run_one_nb();
+ int run_one_ep();
+ void execute_lines(hstcpsvr_conn& conn);
+ void execute_line(char *start, char *finish, hstcpsvr_conn& conn);
+ void do_open_index(char *start, char *finish, hstcpsvr_conn& conn);
+ void do_exec_on_index(char *cmd_begin, char *cmd_end, char *start,
+ char *finish, hstcpsvr_conn& conn);
+ void do_authorization(char *start, char *finish, hstcpsvr_conn& conn);
+};
+
+hstcpsvr_worker::hstcpsvr_worker(const hstcpsvr_worker_arg& arg)
+ : cshared(*arg.cshared), vshared(*arg.vshared), worker_id(arg.worker_id),
+ dbctx(cshared.dbptr->create_context(cshared.for_write_flag)),
+ last_check_time(time(0)), accept_enabled(true), accept_balance(0)
+{
+ #ifdef __linux__
+ if (cshared.sockargs.use_epoll) {
+ epoll_fd.reset(epoll_create(10));
+ if (epoll_fd.get() < 0) {
+ fatal_abort("epoll_create");
+ }
+ epoll_event ev;
+ memset(&ev, 0, sizeof(ev));
+ ev.events = EPOLLIN;
+ ev.data.ptr = 0;
+ if (epoll_ctl(epoll_fd.get(), EPOLL_CTL_ADD, cshared.listen_fd.get(), &ev)
+ != 0) {
+ fatal_abort("epoll_ctl EPOLL_CTL_ADD");
+ }
+ events_vec.resize(10240);
+ }
+ #endif
+ accept_balance = cshared.conf.get_int("accept_balance", 0);
+}
+
+namespace {
+
+struct thr_init {
+ thr_init(const dbcontext_ptr& dc, volatile int& shutdown_flag) : dbctx(dc) {
+ dbctx->init_thread(this, shutdown_flag);
+ }
+ ~thr_init() {
+ dbctx->term_thread();
+ }
+ const dbcontext_ptr& dbctx;
+};
+
+}; // namespace
+
+void
+hstcpsvr_worker::run()
+{
+ thr_init initobj(dbctx, vshared.shutdown);
+
+ #ifdef __linux__
+ if (cshared.sockargs.use_epoll) {
+ while (!vshared.shutdown && dbctx->check_alive()) {
+ run_one_ep();
+ }
+ } else if (cshared.sockargs.nonblocking) {
+ while (!vshared.shutdown && dbctx->check_alive()) {
+ run_one_nb();
+ }
+ } else {
+ /* UNUSED */
+ fatal_abort("run_one");
+ }
+ #else
+ while (!vshared.shutdown && dbctx->check_alive()) {
+ run_one_nb();
+ }
+ #endif
+}
+
+int
+hstcpsvr_worker::run_one_nb()
+{
+ size_t nfds = 0;
+ /* CLIENT SOCKETS */
+ for (hstcpsvr_conns_type::const_iterator i = conns.begin();
+ i != conns.end(); ++i) {
+ if (pfds.size() <= nfds) {
+ pfds.resize(nfds + 1);
+ }
+ pollfd& pfd = pfds[nfds++];
+ pfd.fd = (*i)->fd.get();
+ short ev = 0;
+ if ((*i)->cstate.writebuf.size() != 0) {
+ ev = POLLOUT;
+ } else {
+ ev = POLLIN;
+ }
+ pfd.events = pfd.revents = ev;
+ }
+ /* LISTENER */
+ {
+ const size_t cpt = cshared.nb_conn_per_thread;
+ const short ev = (cpt > nfds) ? POLLIN : 0;
+ if (pfds.size() <= nfds) {
+ pfds.resize(nfds + 1);
+ }
+ pollfd& pfd = pfds[nfds++];
+ pfd.fd = cshared.listen_fd.get();
+ pfd.events = pfd.revents = ev;
+ }
+ /* POLL */
+ const int npollev = poll(&pfds[0], nfds, 1 * 1000);
+ dbctx->set_statistics(conns.size(), npollev);
+ const time_t now = time(0);
+ size_t j = 0;
+ const short mask_in = ~POLLOUT;
+ const short mask_out = POLLOUT | POLLERR | POLLHUP | POLLNVAL;
+ /* READ */
+ for (hstcpsvr_conns_type::iterator i = conns.begin(); i != conns.end();
+ ++i, ++j) {
+ pollfd& pfd = pfds[j];
+ if ((pfd.revents & mask_in) == 0) {
+ continue;
+ }
+ hstcpsvr_conn& conn = **i;
+ if (conn.read_more()) {
+ if (conn.cstate.readbuf.size() > 0) {
+ const char ch = conn.cstate.readbuf.begin()[0];
+ if (ch == 'Q') {
+ vshared.shutdown = 1;
+ } else if (ch == '/') {
+ conn.cstate.readbuf.clear();
+ conn.cstate.writebuf.clear();
+ conn.read_finished = true;
+ conn.write_finished = true;
+ }
+ }
+ conn.nb_last_io = now;
+ }
+ }
+ /* EXECUTE */
+ j = 0;
+ for (hstcpsvr_conns_type::iterator i = conns.begin(); i != conns.end();
+ ++i, ++j) {
+ pollfd& pfd = pfds[j];
+ if ((pfd.revents & mask_in) == 0 || (*i)->cstate.readbuf.size() == 0) {
+ continue;
+ }
+ execute_lines(**i);
+ }
+ /* COMMIT */
+ dbctx->unlock_tables_if();
+ const bool commit_error = dbctx->get_commit_error();
+ dbctx->clear_error();
+ /* WRITE/CLOSE */
+ j = 0;
+ for (hstcpsvr_conns_type::iterator i = conns.begin(); i != conns.end();
+ ++j) {
+ pollfd& pfd = pfds[j];
+ hstcpsvr_conn& conn = **i;
+ hstcpsvr_conns_type::iterator icur = i;
+ ++i;
+ if (commit_error) {
+ conn.reset();
+ continue;
+ }
+ if ((pfd.revents & (mask_out | mask_in)) != 0) {
+ if (conn.write_more()) {
+ conn.nb_last_io = now;
+ }
+ }
+ if (cshared.sockargs.timeout != 0 &&
+ conn.nb_last_io + cshared.sockargs.timeout < now) {
+ conn.reset();
+ }
+ if (conn.closed() || conn.ok_to_close()) {
+ conns.erase_ptr(icur);
+ }
+ }
+ /* ACCEPT */
+ {
+ pollfd& pfd = pfds[nfds - 1];
+ if ((pfd.revents & mask_in) != 0) {
+ std::auto_ptr<hstcpsvr_conn> c(new hstcpsvr_conn());
+ c->nonblocking = true;
+ c->readsize = cshared.readsize;
+ c->accept(cshared);
+ if (c->fd.get() >= 0) {
+ if (fcntl(c->fd.get(), F_SETFL, O_NONBLOCK) != 0) {
+ fatal_abort("F_SETFL O_NONBLOCK");
+ }
+ c->nb_last_io = now;
+ conns.push_back_ptr(c);
+ } else {
+ /* errno == 11 (EAGAIN) is not a fatal error. */
+ DENA_VERBOSE(100, fprintf(stderr,
+ "accept failed: errno=%d (not fatal)\n", errno));
+ }
+ }
+ }
+ DENA_VERBOSE(30, fprintf(stderr, "nb: %p nfds=%zu cns=%zu\n", this, nfds,
+ conns.size()));
+ if (conns.empty()) {
+ dbctx->close_tables_if();
+ }
+ dbctx->set_statistics(conns.size(), 0);
+ return 0;
+}
+
+#ifdef __linux__
+int
+hstcpsvr_worker::run_one_ep()
+{
+ epoll_event *const events = &events_vec[0];
+ const size_t num_events = events_vec.size();
+ const time_t now = time(0);
+ size_t in_count = 0, out_count = 0, accept_count = 0;
+ int nfds = epoll_wait(epoll_fd.get(), events, num_events, 1000);
+ /* READ/ACCEPT */
+ dbctx->set_statistics(conns.size(), nfds);
+ for (int i = 0; i < nfds; ++i) {
+ epoll_event& ev = events[i];
+ if ((ev.events & EPOLLIN) == 0) {
+ continue;
+ }
+ hstcpsvr_conn *const conn = static_cast<hstcpsvr_conn *>(ev.data.ptr);
+ if (conn == 0) {
+ /* listener */
+ ++accept_count;
+ DBG_EP(fprintf(stderr, "IN listener\n"));
+ std::auto_ptr<hstcpsvr_conn> c(new hstcpsvr_conn());
+ c->nonblocking = true;
+ c->readsize = cshared.readsize;
+ c->accept(cshared);
+ if (c->fd.get() >= 0) {
+ if (fcntl(c->fd.get(), F_SETFL, O_NONBLOCK) != 0) {
+ fatal_abort("F_SETFL O_NONBLOCK");
+ }
+ epoll_event cev;
+ memset(&cev, 0, sizeof(cev));
+ cev.events = EPOLLIN | EPOLLOUT | EPOLLET;
+ cev.data.ptr = c.get();
+ c->nb_last_io = now;
+ const int fd = c->fd.get();
+ conns.push_back_ptr(c);
+ conns.back()->conns_iter = --conns.end();
+ if (epoll_ctl(epoll_fd.get(), EPOLL_CTL_ADD, fd, &cev) != 0) {
+ fatal_abort("epoll_ctl EPOLL_CTL_ADD");
+ }
+ } else {
+ DENA_VERBOSE(100, fprintf(stderr,
+ "accept failed: errno=%d (not fatal)\n", errno));
+ }
+ } else {
+ /* client connection */
+ ++in_count;
+ DBG_EP(fprintf(stderr, "IN client\n"));
+ bool more_data = false;
+ while (conn->read_more(&more_data)) {
+ DBG_EP(fprintf(stderr, "IN client read_more\n"));
+ conn->nb_last_io = now;
+ if (!more_data) {
+ break;
+ }
+ }
+ }
+ }
+ /* EXECUTE */
+ for (int i = 0; i < nfds; ++i) {
+ epoll_event& ev = events[i];
+ hstcpsvr_conn *const conn = static_cast<hstcpsvr_conn *>(ev.data.ptr);
+ if ((ev.events & EPOLLIN) == 0 || conn == 0 ||
+ conn->cstate.readbuf.size() == 0) {
+ continue;
+ }
+ const char ch = conn->cstate.readbuf.begin()[0];
+ if (ch == 'Q') {
+ vshared.shutdown = 1;
+ } else if (ch == '/') {
+ conn->cstate.readbuf.clear();
+ conn->cstate.writebuf.clear();
+ conn->read_finished = true;
+ conn->write_finished = true;
+ } else {
+ execute_lines(*conn);
+ }
+ }
+ /* COMMIT */
+ dbctx->unlock_tables_if();
+ const bool commit_error = dbctx->get_commit_error();
+ dbctx->clear_error();
+ /* WRITE */
+ for (int i = 0; i < nfds; ++i) {
+ epoll_event& ev = events[i];
+ hstcpsvr_conn *const conn = static_cast<hstcpsvr_conn *>(ev.data.ptr);
+ if (commit_error && conn != 0) {
+ conn->reset();
+ continue;
+ }
+ if ((ev.events & EPOLLOUT) == 0) {
+ continue;
+ }
+ ++out_count;
+ if (conn == 0) {
+ /* listener */
+ DBG_EP(fprintf(stderr, "OUT listener\n"));
+ } else {
+ /* client connection */
+ DBG_EP(fprintf(stderr, "OUT client\n"));
+ bool more_data = false;
+ while (conn->write_more(&more_data)) {
+ DBG_EP(fprintf(stderr, "OUT client write_more\n"));
+ conn->nb_last_io = now;
+ if (!more_data) {
+ break;
+ }
+ }
+ }
+ }
+ /* CLOSE */
+ for (int i = 0; i < nfds; ++i) {
+ epoll_event& ev = events[i];
+ hstcpsvr_conn *const conn = static_cast<hstcpsvr_conn *>(ev.data.ptr);
+ if (conn != 0 && conn->ok_to_close()) {
+ DBG_EP(fprintf(stderr, "CLOSE close\n"));
+ conns.erase_ptr(conn->conns_iter);
+ }
+ }
+ /* TIMEOUT & cleanup */
+ if (last_check_time + 10 < now) {
+ for (hstcpsvr_conns_type::iterator i = conns.begin();
+ i != conns.end(); ) {
+ hstcpsvr_conns_type::iterator icur = i;
+ ++i;
+ if (cshared.sockargs.timeout != 0 &&
+ (*icur)->nb_last_io + cshared.sockargs.timeout < now) {
+ conns.erase_ptr((*icur)->conns_iter);
+ }
+ }
+ last_check_time = now;
+ DENA_VERBOSE(20, fprintf(stderr, "ep: %p nfds=%d cns=%zu\n", this, nfds,
+ conns.size()));
+ }
+ DENA_VERBOSE(30, fprintf(stderr, "%p in=%zu out=%zu ac=%zu, cns=%zu\n",
+ this, in_count, out_count, accept_count, conns.size()));
+ if (conns.empty()) {
+ dbctx->close_tables_if();
+ }
+ /* STATISTICS */
+ const size_t num_conns = conns.size();
+ dbctx->set_statistics(num_conns, 0);
+ /* ENABLE/DISABLE ACCEPT */
+ if (accept_balance != 0) {
+ cshared.thread_num_conns[worker_id] = num_conns;
+ size_t total_num_conns = 0;
+ for (long i = 0; i < cshared.num_threads; ++i) {
+ total_num_conns += cshared.thread_num_conns[i];
+ }
+ bool e_acc = false;
+ if (num_conns < 10 ||
+ total_num_conns * 2 > num_conns * cshared.num_threads) {
+ e_acc = true;
+ }
+ epoll_event ev;
+ memset(&ev, 0, sizeof(ev));
+ ev.events = EPOLLIN;
+ ev.data.ptr = 0;
+ if (e_acc == accept_enabled) {
+ } else if (e_acc) {
+ if (epoll_ctl(epoll_fd.get(), EPOLL_CTL_ADD, cshared.listen_fd.get(), &ev)
+ != 0) {
+ fatal_abort("epoll_ctl EPOLL_CTL_ADD");
+ }
+ } else {
+ if (epoll_ctl(epoll_fd.get(), EPOLL_CTL_DEL, cshared.listen_fd.get(), &ev)
+ != 0) {
+ fatal_abort("epoll_ctl EPOLL_CTL_ADD");
+ }
+ }
+ accept_enabled = e_acc;
+ }
+ return 0;
+}
+#endif
+
+void
+hstcpsvr_worker::execute_lines(hstcpsvr_conn& conn)
+{
+ dbconnstate& cstate = conn.cstate;
+ char *buf_end = cstate.readbuf.end();
+ char *line_begin = cstate.readbuf.begin();
+ while (true) {
+ char *const nl = memchr_char(line_begin, '\n', buf_end - line_begin);
+ if (nl == 0) {
+ break;
+ }
+ char *const lf = (line_begin != nl && nl[-1] == '\r') ? nl - 1 : nl;
+ execute_line(line_begin, lf, conn);
+ line_begin = nl + 1;
+ }
+ cstate.readbuf.erase_front(line_begin - cstate.readbuf.begin());
+}
+
+void
+hstcpsvr_worker::execute_line(char *start, char *finish, hstcpsvr_conn& conn)
+{
+ /* safe to modify, safe to dereference 'finish' */
+ char *const cmd_begin = start;
+ read_token(start, finish);
+ char *const cmd_end = start;
+ skip_one(start, finish);
+ if (cmd_begin == cmd_end) {
+ return conn.dbcb_resp_short(2, "cmd");
+ }
+ if (cmd_begin + 1 == cmd_end) {
+ if (cmd_begin[0] == 'P') {
+ if (cshared.require_auth && !conn.authorized) {
+ return conn.dbcb_resp_short(3, "unauth");
+ }
+ return do_open_index(start, finish, conn);
+ }
+ if (cmd_begin[0] == 'A') {
+ return do_authorization(start, finish, conn);
+ }
+ }
+ if (cmd_begin[0] >= '0' && cmd_begin[0] <= '9') {
+ if (cshared.require_auth && !conn.authorized) {
+ return conn.dbcb_resp_short(3, "unauth");
+ }
+ return do_exec_on_index(cmd_begin, cmd_end, start, finish, conn);
+ }
+ return conn.dbcb_resp_short(2, "cmd");
+}
+
+void
+hstcpsvr_worker::do_open_index(char *start, char *finish, hstcpsvr_conn& conn)
+{
+ const size_t pst_id = read_ui32(start, finish);
+ skip_one(start, finish);
+ /* dbname */
+ char *const dbname_begin = start;
+ read_token(start, finish);
+ char *const dbname_end = start;
+ skip_one(start, finish);
+ /* tblname */
+ char *const tblname_begin = start;
+ read_token(start, finish);
+ char *const tblname_end = start;
+ skip_one(start, finish);
+ /* idxname */
+ char *const idxname_begin = start;
+ read_token(start, finish);
+ char *const idxname_end = start;
+ skip_one(start, finish);
+ /* retfields */
+ char *const retflds_begin = start;
+ read_token(start, finish);
+ char *const retflds_end = start;
+ skip_one(start, finish);
+ /* filfields */
+ char *const filflds_begin = start;
+ read_token(start, finish);
+ char *const filflds_end = start;
+ dbname_end[0] = 0;
+ tblname_end[0] = 0;
+ idxname_end[0] = 0;
+ retflds_end[0] = 0;
+ filflds_end[0] = 0;
+ return dbctx->cmd_open_index(conn, pst_id, dbname_begin, tblname_begin,
+ idxname_begin, retflds_begin, filflds_begin);
+}
+
+void
+hstcpsvr_worker::do_exec_on_index(char *cmd_begin, char *cmd_end, char *start,
+ char *finish, hstcpsvr_conn& conn)
+{
+ cmd_exec_args args;
+ const size_t pst_id = read_ui32(cmd_begin, cmd_end);
+ if (pst_id >= conn.cstate.prep_stmts.size()) {
+ return conn.dbcb_resp_short(2, "stmtnum");
+ }
+ args.pst = &conn.cstate.prep_stmts[pst_id];
+ char *const op_begin = start;
+ read_token(start, finish);
+ char *const op_end = start;
+ args.op = string_ref(op_begin, op_end);
+ skip_one(start, finish);
+ const uint32_t fldnum = read_ui32(start, finish);
+ string_ref flds[fldnum]; /* GNU */
+ args.kvals = flds;
+ args.kvalslen = fldnum;
+ for (size_t i = 0; i < fldnum; ++i) {
+ skip_one(start, finish);
+ char *const f_begin = start;
+ read_token(start, finish);
+ char *const f_end = start;
+ if (is_null_expression(f_begin, f_end)) {
+ /* null */
+ flds[i] = string_ref();
+ } else {
+ /* non-null */
+ char *wp = f_begin;
+ unescape_string(wp, f_begin, f_end);
+ flds[i] = string_ref(f_begin, wp - f_begin);
+ }
+ }
+ skip_one(start, finish);
+ args.limit = read_ui32(start, finish);
+ skip_one(start, finish);
+ args.skip = read_ui32(start, finish);
+ if (start == finish) {
+ /* simple query */
+ return dbctx->cmd_exec_on_index(conn, args);
+ }
+ /* has filters or modops */
+ skip_one(start, finish);
+ /* filters */
+ size_t filters_count = 0;
+ while (start != finish && (start[0] == 'W' || start[0] == 'F')) {
+ char *const filter_type_begin = start;
+ read_token(start, finish);
+ char *const filter_type_end = start;
+ skip_one(start, finish);
+ char *const filter_op_begin = start;
+ read_token(start, finish);
+ char *const filter_op_end = start;
+ skip_one(start, finish);
+ const uint32_t ff_offset = read_ui32(start, finish);
+ skip_one(start, finish);
+ char *const filter_val_begin = start;
+ read_token(start, finish);
+ char *const filter_val_end = start;
+ skip_one(start, finish);
+ if (filters_work.size() <= filters_count) {
+ filters_work.resize(filters_count + 1);
+ }
+ record_filter& fi = filters_work[filters_count];
+ if (filter_type_end != filter_type_begin + 1) {
+ return conn.dbcb_resp_short(2, "filtertype");
+ }
+ fi.filter_type = (filter_type_begin[0] == 'W')
+ ? record_filter_type_break : record_filter_type_skip;
+ const uint32_t num_filflds = args.pst->get_filter_fields().size();
+ if (ff_offset >= num_filflds) {
+ return conn.dbcb_resp_short(2, "filterfld");
+ }
+ fi.op = string_ref(filter_op_begin, filter_op_end);
+ fi.ff_offset = ff_offset;
+ if (is_null_expression(filter_val_begin, filter_val_end)) {
+ /* null */
+ fi.val = string_ref();
+ } else {
+ /* non-null */
+ char *wp = filter_val_begin;
+ unescape_string(wp, filter_val_begin, filter_val_end);
+ fi.val = string_ref(filter_val_begin, wp - filter_val_begin);
+ }
+ ++filters_count;
+ }
+ if (filters_count > 0) {
+ if (filters_work.size() <= filters_count) {
+ filters_work.resize(filters_count + 1);
+ }
+ filters_work[filters_count].op = string_ref(); /* sentinel */
+ args.filters = &filters_work[0];
+ } else {
+ args.filters = 0;
+ }
+ if (start == finish) {
+ /* no modops */
+ return dbctx->cmd_exec_on_index(conn, args);
+ }
+ /* has modops */
+ char *const mod_op_begin = start;
+ read_token(start, finish);
+ char *const mod_op_end = start;
+ args.mod_op = string_ref(mod_op_begin, mod_op_end);
+ const size_t num_uvals = args.pst->get_ret_fields().size();
+ string_ref uflds[num_uvals]; /* GNU */
+ for (size_t i = 0; i < num_uvals; ++i) {
+ skip_one(start, finish);
+ char *const f_begin = start;
+ read_token(start, finish);
+ char *const f_end = start;
+ if (is_null_expression(f_begin, f_end)) {
+ /* null */
+ uflds[i] = string_ref();
+ } else {
+ /* non-null */
+ char *wp = f_begin;
+ unescape_string(wp, f_begin, f_end);
+ uflds[i] = string_ref(f_begin, wp - f_begin);
+ }
+ }
+ args.uvals = uflds;
+ return dbctx->cmd_exec_on_index(conn, args);
+}
+
+void
+hstcpsvr_worker::do_authorization(char *start, char *finish,
+ hstcpsvr_conn& conn)
+{
+ /* auth type */
+ char *const authtype_begin = start;
+ read_token(start, finish);
+ char *const authtype_end = start;
+ const size_t authtype_len = authtype_end - authtype_begin;
+ skip_one(start, finish);
+ /* key */
+ char *const key_begin = start;
+ read_token(start, finish);
+ char *const key_end = start;
+ const size_t key_len = key_end - key_begin;
+ authtype_end[0] = 0;
+ key_end[0] = 0;
+ char *wp = key_begin;
+ unescape_string(wp, key_begin, key_end);
+ if (authtype_len != 1 || authtype_begin[0] != '1') {
+ return conn.dbcb_resp_short(2, "authtype");
+ }
+ if (cshared.plain_secret.size() == key_len &&
+ memcmp(cshared.plain_secret.data(), key_begin, key_len) == 0) {
+ conn.authorized = true;
+ } else {
+ conn.authorized = false;
+ }
+ if (!conn.authorized) {
+ return conn.dbcb_resp_short(3, "unauth");
+ } else {
+ return conn.dbcb_resp_short(0, "");
+ }
+}
+
+hstcpsvr_worker_ptr
+hstcpsvr_worker_i::create(const hstcpsvr_worker_arg& arg)
+{
+ return hstcpsvr_worker_ptr(new hstcpsvr_worker(arg));
+}
+
+};
+
diff --git a/plugin/handler_socket/handlersocket/hstcpsvr_worker.hpp b/plugin/handler_socket/handlersocket/hstcpsvr_worker.hpp
new file mode 100644
index 00000000000..497581c27a7
--- /dev/null
+++ b/plugin/handler_socket/handlersocket/hstcpsvr_worker.hpp
@@ -0,0 +1,35 @@
+
+// vim:sw=2:ai
+
+/*
+ * Copyright (C) 2010 DeNA Co.,Ltd.. All rights reserved.
+ * See COPYRIGHT.txt for details.
+ */
+
+#ifndef DENA_HSTCPSVR_WORKER_HPP
+#define DENA_HSTCPSVR_WORKER_HPP
+
+#include "hstcpsvr.hpp"
+
+namespace dena {
+
+struct hstcpsvr_worker_i;
+typedef std::auto_ptr<hstcpsvr_worker_i> hstcpsvr_worker_ptr;
+
+struct hstcpsvr_worker_arg {
+ const hstcpsvr_shared_c *cshared;
+ volatile hstcpsvr_shared_v *vshared;
+ long worker_id;
+ hstcpsvr_worker_arg() : cshared(0), vshared(0), worker_id(0) { }
+};
+
+struct hstcpsvr_worker_i {
+ virtual ~hstcpsvr_worker_i() { }
+ virtual void run() = 0;
+ static hstcpsvr_worker_ptr create(const hstcpsvr_worker_arg& arg);
+};
+
+};
+
+#endif
+
diff --git a/plugin/handler_socket/handlersocket/mysql_incl.hpp b/plugin/handler_socket/handlersocket/mysql_incl.hpp
new file mode 100644
index 00000000000..c7b3fce0d1d
--- /dev/null
+++ b/plugin/handler_socket/handlersocket/mysql_incl.hpp
@@ -0,0 +1,49 @@
+
+// vim:sw=2:ai
+
+/*
+ * Copyright (C) 2010 DeNA Co.,Ltd.. All rights reserved.
+ * See COPYRIGHT.txt for details.
+ */
+
+#ifndef DENA_MYSQL_INCL_HPP
+#define DENA_MYSQL_INCL_HPP
+
+#ifndef HAVE_CONFIG_H
+#define HAVE_CONFIG_H
+#endif
+
+#define MYSQL_DYNAMIC_PLUGIN
+#define MYSQL_SERVER 1
+
+#include <my_config.h>
+#include <mysql_version.h>
+
+#if MYSQL_VERSION_ID >= 50505
+#include <my_pthread.h>
+#include <sql_priv.h>
+#include "sql_class.h"
+#include "unireg.h"
+#include "lock.h"
+#include "key.h" // key_copy()
+#include <my_global.h>
+#include <mysql/plugin.h>
+#include <transaction.h>
+#include <sql_base.h>
+// FIXME FIXME FIXME
+#define safeFree(X) my_free(X)
+#define pthread_cond_timedwait mysql_cond_timedwait
+#define pthread_mutex_lock mysql_mutex_lock
+#define pthread_mutex_unlock mysql_mutex_unlock
+#define current_stmt_binlog_row_based is_current_stmt_binlog_format_row
+#define clear_current_stmt_binlog_row_based clear_current_stmt_binlog_format_row
+
+#else
+#include "mysql_priv.h"
+#endif
+
+#undef min
+#undef max
+
+#endif
+
diff --git a/plugin/handler_socket/libhsclient/COPYRIGHT.txt b/plugin/handler_socket/libhsclient/COPYRIGHT.txt
new file mode 100644
index 00000000000..41dda1279e2
--- /dev/null
+++ b/plugin/handler_socket/libhsclient/COPYRIGHT.txt
@@ -0,0 +1,27 @@
+
+ Copyright (c) 2010 DeNA Co.,Ltd.
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of DeNA Co.,Ltd. nor the names of its contributors
+ may be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY DeNA Co.,Ltd. "AS IS" AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ EVENT SHALL DeNA Co.,Ltd. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
diff --git a/plugin/handler_socket/libhsclient/Makefile.am b/plugin/handler_socket/libhsclient/Makefile.am
new file mode 100644
index 00000000000..0a796fa4f43
--- /dev/null
+++ b/plugin/handler_socket/libhsclient/Makefile.am
@@ -0,0 +1,13 @@
+CXXFLAGS += -fimplicit-templates
+instdir = $(includedir)/handlersocket
+# TODO: these headers should be in dena/
+inst_HEADERS = allocator.hpp config.hpp mutex.hpp string_util.hpp \
+ auto_addrinfo.hpp escape.hpp socket.hpp thread.hpp auto_file.hpp \
+ fatal.hpp string_buffer.hpp util.hpp auto_ptrcontainer.hpp \
+ hstcpcli.hpp string_ref.hpp
+lib_LTLIBRARIES = libhsclient.la
+libhsclient_la_SOURCES = config.cpp escape.cpp fatal.cpp hstcpcli.cpp \
+ socket.cpp string_util.cpp
+#libhsclient_la_LDFLAGS = -static
+libhsclient_la_CFLAGS = $(AM_CFLAGS)
+libhsclient_la_CXXFLAGS = $(AM_CFLAGS)
diff --git a/plugin/handler_socket/libhsclient/Makefile.plain b/plugin/handler_socket/libhsclient/Makefile.plain
new file mode 100644
index 00000000000..9e6277b6253
--- /dev/null
+++ b/plugin/handler_socket/libhsclient/Makefile.plain
@@ -0,0 +1,27 @@
+
+CXX = g++ -Wall -g -fno-rtti -fno-exceptions -fPIC -DPIC
+LDFLAGS =
+
+CXXFLAGS += -O3 -DNDEBUG
+
+COMMON_OBJS = config.o fatal.o socket.o string_util.o escape.o
+HSCLIENT_OBJS = $(COMMON_OBJS) hstcpcli.o
+
+all: libhsclient.a
+
+libhsclient.a: $(HSCLIENT_OBJS)
+ $(AR) rc $@ $^
+ $(AR) s $@
+
+clean:
+ rm -f *.a *.so *.o
+
+LIBDIR = $(shell \
+ if [ -e /usr/lib64/mysql ]; then echo /usr/lib64; else echo /usr/lib; fi)
+
+install: libhsclient.a
+ sudo sh -c 'cp libhsclient.a libhsclient.a.cpy && \
+ mv libhsclient.a.cpy $(LIBDIR)/libhsclient.a && \
+ mkdir -p /usr/include/handlersocket && \
+ cp -a *.hpp /usr/include/handlersocket/'
+
diff --git a/plugin/handler_socket/libhsclient/allocator.hpp b/plugin/handler_socket/libhsclient/allocator.hpp
new file mode 100644
index 00000000000..d7cd33ffad3
--- /dev/null
+++ b/plugin/handler_socket/libhsclient/allocator.hpp
@@ -0,0 +1,37 @@
+
+// vim:sw=2:ai
+
+/*
+ * Copyright (C) 2010 DeNA Co.,Ltd.. All rights reserved.
+ * See COPYRIGHT.txt for details.
+ */
+
+#ifndef DENA_ALLOCATOR_HPP
+#define DENA_ALLOCATOR_HPP
+
+#include <stdlib.h>
+#include <string.h>
+
+#if 0
+extern "C" {
+#include <tlsf.h>
+};
+#define DENA_MALLOC(x) tlsf_malloc(x)
+#define DENA_REALLOC(x, y) tlsf_realloc(x, y)
+#define DENA_FREE(x) tlsf_free(x)
+#define DENA_NEWCHAR(x) static_cast<char *>(tlsf_malloc(x))
+#define DENA_DELETE(x) tlsf_free(x)
+typedef std::allocator<int> allocator_type;
+#endif
+
+#if 1
+#define DENA_MALLOC(x) malloc(x)
+#define DENA_REALLOC(x, y) realloc(x, y)
+#define DENA_FREE(x) free(x)
+#define DENA_NEWCHAR(x) (new char[x])
+#define DENA_DELETE(x) (delete [] x)
+typedef std::allocator<int> allocator_type;
+#endif
+
+#endif
+
diff --git a/plugin/handler_socket/libhsclient/auto_addrinfo.hpp b/plugin/handler_socket/libhsclient/auto_addrinfo.hpp
new file mode 100644
index 00000000000..5be3d02c75d
--- /dev/null
+++ b/plugin/handler_socket/libhsclient/auto_addrinfo.hpp
@@ -0,0 +1,50 @@
+
+// vim:sw=2:ai
+
+/*
+ * Copyright (C) 2010 DeNA Co.,Ltd.. All rights reserved.
+ * See COPYRIGHT.txt for details.
+ */
+
+#ifndef DENA_AUTO_ADDRINFO_HPP
+#define DENA_AUTO_ADDRINFO_HPP
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <string.h>
+#include "util.hpp"
+
+namespace dena {
+
+struct auto_addrinfo : private noncopyable {
+ auto_addrinfo() : addr(0) { }
+ ~auto_addrinfo() {
+ reset();
+ }
+ void reset(addrinfo *a = 0) {
+ if (addr != 0) {
+ freeaddrinfo(addr);
+ }
+ addr = a;
+ }
+ const addrinfo *get() const { return addr; }
+ int resolve(const char *node, const char *service, int flags = 0,
+ int family = AF_UNSPEC, int socktype = SOCK_STREAM, int protocol = 0) {
+ reset();
+ addrinfo hints;
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_flags = flags;
+ hints.ai_family = family;
+ hints.ai_socktype = socktype;
+ hints.ai_protocol = protocol;
+ return getaddrinfo(node, service, &hints, &addr);
+ }
+ private:
+ addrinfo *addr;
+};
+
+};
+
+#endif
+
diff --git a/plugin/handler_socket/libhsclient/auto_file.hpp b/plugin/handler_socket/libhsclient/auto_file.hpp
new file mode 100644
index 00000000000..841351e54cd
--- /dev/null
+++ b/plugin/handler_socket/libhsclient/auto_file.hpp
@@ -0,0 +1,64 @@
+
+// vim:sw=2:ai
+
+/*
+ * Copyright (C) 2010 DeNA Co.,Ltd.. All rights reserved.
+ * See COPYRIGHT.txt for details.
+ */
+
+#ifndef DENA_AUTO_FILE_HPP
+#define DENA_AUTO_FILE_HPP
+
+#include <unistd.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <stdio.h>
+
+#include "util.hpp"
+
+namespace dena {
+
+struct auto_file : private noncopyable {
+ auto_file() : fd(-1) { }
+ ~auto_file() {
+ reset();
+ }
+ int get() const { return fd; }
+ int close() {
+ if (fd < 0) {
+ return 0;
+ }
+ const int r = ::close(fd);
+ fd = -1;
+ return r;
+ }
+ void reset(int x = -1) {
+ if (fd >= 0) {
+ this->close();
+ }
+ fd = x;
+ }
+ private:
+ int fd;
+};
+
+struct auto_dir : private noncopyable {
+ auto_dir() : dp(0) { }
+ ~auto_dir() {
+ reset();
+ }
+ DIR *get() const { return dp; }
+ void reset(DIR *d = 0) {
+ if (dp != 0) {
+ closedir(dp);
+ }
+ dp = d;
+ }
+ private:
+ DIR *dp;
+};
+
+};
+
+#endif
+
diff --git a/plugin/handler_socket/libhsclient/auto_ptrcontainer.hpp b/plugin/handler_socket/libhsclient/auto_ptrcontainer.hpp
new file mode 100644
index 00000000000..314bc1516ff
--- /dev/null
+++ b/plugin/handler_socket/libhsclient/auto_ptrcontainer.hpp
@@ -0,0 +1,67 @@
+
+// vim:sw=2:ai
+
+/*
+ * Copyright (C) 2010 DeNA Co.,Ltd.. All rights reserved.
+ * See COPYRIGHT.txt for details.
+ */
+
+#ifndef DENA_AUTO_PTRCONTAINER_HPP
+#define DENA_AUTO_PTRCONTAINER_HPP
+
+namespace dena {
+
+template <typename Tcnt>
+struct auto_ptrcontainer {
+ typedef Tcnt container_type;
+ typedef typename container_type::value_type value_type;
+ typedef typename container_type::pointer pointer;
+ typedef typename container_type::reference reference;
+ typedef typename container_type::const_reference const_reference;
+ typedef typename container_type::size_type size_type;
+ typedef typename container_type::difference_type difference_type;
+ typedef typename container_type::iterator iterator;
+ typedef typename container_type::const_iterator const_iterator;
+ typedef typename container_type::reverse_iterator reverse_iterator;
+ typedef typename container_type::const_reverse_iterator
+ const_reverse_iterator;
+ iterator begin() { return cnt.begin(); }
+ const_iterator begin() const { return cnt.begin(); }
+ iterator end() { return cnt.end(); }
+ const_iterator end() const { return cnt.end(); }
+ reverse_iterator rbegin() { return cnt.rbegin(); }
+ reverse_iterator rend() { return cnt.rend(); }
+ const_reverse_iterator rbegin() const { return cnt.rbegin(); }
+ const_reverse_iterator rend() const { return cnt.rend(); }
+ size_type size() const { return cnt.size(); }
+ size_type max_size() const { return cnt.max_size(); }
+ bool empty() const { return cnt.empty(); }
+ reference front() { return cnt.front(); }
+ const_reference front() const { cnt.front(); }
+ reference back() { return cnt.back(); }
+ const_reference back() const { cnt.back(); }
+ void swap(auto_ptrcontainer& x) { cnt.swap(x.cnt); }
+ ~auto_ptrcontainer() {
+ for (iterator i = begin(); i != end(); ++i) {
+ delete *i;
+ }
+ }
+ template <typename Tap> void push_back_ptr(Tap& ap) {
+ cnt.push_back(ap.get());
+ ap.release();
+ }
+ void erase_ptr(iterator i) {
+ delete *i;
+ cnt.erase(i);
+ }
+ reference operator [](size_type n) { return cnt[n]; }
+ const_reference operator [](size_type n) const { return cnt[n]; }
+ void clear() { cnt.clear(); }
+ private:
+ Tcnt cnt;
+};
+
+};
+
+#endif
+
diff --git a/plugin/handler_socket/libhsclient/config.cpp b/plugin/handler_socket/libhsclient/config.cpp
new file mode 100644
index 00000000000..3c90b36db44
--- /dev/null
+++ b/plugin/handler_socket/libhsclient/config.cpp
@@ -0,0 +1,67 @@
+
+// vim:sw=2:ai
+
+/*
+ * Copyright (C) 2010 DeNA Co.,Ltd.. All rights reserved.
+ * See COPYRIGHT.txt for details.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "config.hpp"
+
+namespace dena {
+
+unsigned int verbose_level = 0;
+
+std::string
+config::get_str(const std::string& key, const std::string& def) const
+{
+ const_iterator iter = this->find(key);
+ if (iter == this->end()) {
+ DENA_VERBOSE(10, fprintf(stderr, "CONFIG: %s=%s(default)\n", key.c_str(),
+ def.c_str()));
+ return def;
+ }
+ DENA_VERBOSE(10, fprintf(stderr, "CONFIG: %s=%s\n", key.c_str(),
+ iter->second.c_str()));
+ return iter->second;
+}
+
+long long
+config::get_int(const std::string& key, long long def) const
+{
+ const_iterator iter = this->find(key);
+ if (iter == this->end()) {
+ DENA_VERBOSE(10, fprintf(stderr, "CONFIG: %s=%lld(default)\n", key.c_str(),
+ def));
+ return def;
+ }
+ const long long r = atoll(iter->second.c_str());
+ DENA_VERBOSE(10, fprintf(stderr, "CONFIG: %s=%lld\n", key.c_str(), r));
+ return r;
+}
+
+void
+parse_args(int argc, char **argv, config& conf)
+{
+ for (int i = 1; i < argc; ++i) {
+ const char *const arg = argv[i];
+ const char *const eq = strchr(arg, '=');
+ if (eq == 0) {
+ continue;
+ }
+ const std::string key(arg, eq - arg);
+ const std::string val(eq + 1);
+ conf[key] = val;
+ }
+ config::const_iterator iter = conf.find("verbose");
+ if (iter != conf.end()) {
+ verbose_level = atoi(iter->second.c_str());
+ }
+}
+
+};
+
diff --git a/plugin/handler_socket/libhsclient/config.hpp b/plugin/handler_socket/libhsclient/config.hpp
new file mode 100644
index 00000000000..c9f16c76fa9
--- /dev/null
+++ b/plugin/handler_socket/libhsclient/config.hpp
@@ -0,0 +1,32 @@
+
+// vim:sw=2:ai
+
+/*
+ * Copyright (C) 2010 DeNA Co.,Ltd.. All rights reserved.
+ * See COPYRIGHT.txt for details.
+ */
+
+#ifndef DENA_CONFIG_HPP
+#define DENA_CONFIG_HPP
+
+#include <string>
+#include <map>
+
+#define DENA_VERBOSE(lv, x) if (dena::verbose_level >= (lv)) { (x); }
+
+namespace dena {
+
+struct config : public std::map<std::string, std::string> {
+ std::string get_str(const std::string& key, const std::string& def = "")
+ const;
+ long long get_int(const std::string& key, long long def = 0) const;
+};
+
+void parse_args(int argc, char **argv, config& conf);
+
+extern unsigned int verbose_level;
+
+};
+
+#endif
+
diff --git a/plugin/handler_socket/libhsclient/escape.cpp b/plugin/handler_socket/libhsclient/escape.cpp
new file mode 100644
index 00000000000..8bbe3ca8c27
--- /dev/null
+++ b/plugin/handler_socket/libhsclient/escape.cpp
@@ -0,0 +1,117 @@
+
+// vim:sw=2:ai
+
+/*
+ * Copyright (C) 2010 DeNA Co.,Ltd.. All rights reserved.
+ * See COPYRIGHT.txt for details.
+ */
+
+#include <stdio.h>
+
+#include "escape.hpp"
+#include "string_buffer.hpp"
+#include "fatal.hpp"
+#include "string_util.hpp"
+
+#define DBG_OP(x)
+#define DBG_BUF(x)
+
+namespace dena {
+
+enum special_char_t {
+ special_char_escape_prefix = 0x01, /* SOH */
+ special_char_noescape_min = 0x10, /* DLE */
+ special_char_escape_shift = 0x40, /* '@' */
+};
+
+void
+escape_string(char *& wp, const char *start, const char *finish)
+{
+ while (start != finish) {
+ const unsigned char c = *start;
+ if (c >= special_char_noescape_min) {
+ wp[0] = c; /* no need to escape */
+ } else {
+ wp[0] = special_char_escape_prefix;
+ ++wp;
+ wp[0] = c + special_char_escape_shift;
+ }
+ ++start;
+ ++wp;
+ }
+}
+
+void
+escape_string(string_buffer& ar, const char *start, const char *finish)
+{
+ const size_t buflen = (finish - start) * 2;
+ char *const wp_begin = ar.make_space(buflen);
+ char *wp = wp_begin;
+ escape_string(wp, start, finish);
+ ar.space_wrote(wp - wp_begin);
+}
+
+bool
+unescape_string(char *& wp, const char *start, const char *finish)
+{
+ /* works even if wp == start */
+ while (start != finish) {
+ const unsigned char c = *start;
+ if (c != special_char_escape_prefix) {
+ wp[0] = c;
+ } else if (start + 1 != finish) {
+ ++start;
+ const unsigned char cn = *start;
+ if (cn < special_char_escape_shift) {
+ return false;
+ }
+ wp[0] = cn - special_char_escape_shift;
+ } else {
+ return false;
+ }
+ ++start;
+ ++wp;
+ }
+ return true;
+}
+
+bool
+unescape_string(string_buffer& ar, const char *start, const char *finish)
+{
+ const size_t buflen = finish - start;
+ char *const wp_begin = ar.make_space(buflen);
+ char *wp = wp_begin;
+ const bool r = unescape_string(wp, start, finish);
+ ar.space_wrote(wp - wp_begin);
+ return r;
+}
+
+uint32_t
+read_ui32(char *& start, char *finish)
+{
+ char *const n_begin = start;
+ read_token(start, finish);
+ char *const n_end = start;
+ uint32_t v = 0;
+ for (char *p = n_begin; p != n_end; ++p) {
+ const char ch = p[0];
+ if (ch >= '0' && ch <= '9') {
+ v *= 10;
+ v += (ch - '0');
+ }
+ }
+ return v;
+}
+
+void
+write_ui32(string_buffer& buf, uint32_t v)
+{
+ char *wp = buf.make_space(32);
+ int len = snprintf(wp, 32, "%u", v);
+ if (len > 0) {
+ buf.space_wrote(len);
+ }
+}
+
+};
+
diff --git a/plugin/handler_socket/libhsclient/escape.hpp b/plugin/handler_socket/libhsclient/escape.hpp
new file mode 100644
index 00000000000..9e2207f6c69
--- /dev/null
+++ b/plugin/handler_socket/libhsclient/escape.hpp
@@ -0,0 +1,65 @@
+
+// vim:sw=2:ai
+
+/*
+ * Copyright (C) 2010 DeNA Co.,Ltd.. All rights reserved.
+ * See COPYRIGHT.txt for details.
+ */
+
+#include <stdint.h>
+
+#include "string_buffer.hpp"
+#include "string_ref.hpp"
+#include "string_util.hpp"
+
+#ifndef DENA_ESCAPE_HPP
+#define DENA_ESCAPE_HPP
+
+namespace dena {
+
+void escape_string(char *& wp, const char *start, const char *finish);
+void escape_string(string_buffer& ar, const char *start, const char *finish);
+bool unescape_string(char *& wp, const char *start, const char *finish);
+ /* unescaped_string() works even if wp == start */
+bool unescape_string(string_buffer& ar, const char *start, const char *finish);
+
+uint32_t read_ui32(char *& start, char *finish);
+void write_ui32(string_buffer& buf, uint32_t v);
+
+inline bool
+is_null_expression(const char *start, const char *finish)
+{
+ return (finish == start + 1 && start[0] == 0);
+}
+
+inline void
+read_token(char *& start, char *finish)
+{
+ char *const p = memchr_char(start, '\t', finish - start);
+ if (p == 0) {
+ start = finish;
+ } else {
+ start = p;
+ }
+}
+
+inline void
+skip_token_delim_fold(char *& start, char *finish)
+{
+ while (start != finish && start[0] == '\t') {
+ ++start;
+ }
+}
+
+inline void
+skip_one(char *& start, char *finish)
+{
+ if (start != finish) {
+ ++start;
+ }
+}
+
+};
+
+#endif
+
diff --git a/plugin/handler_socket/libhsclient/fatal.cpp b/plugin/handler_socket/libhsclient/fatal.cpp
new file mode 100644
index 00000000000..8f8751da382
--- /dev/null
+++ b/plugin/handler_socket/libhsclient/fatal.cpp
@@ -0,0 +1,36 @@
+
+// vim:sw=2:ai
+
+/*
+ * Copyright (C) 2010 DeNA Co.,Ltd.. All rights reserved.
+ * See COPYRIGHT.txt for details.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <syslog.h>
+
+#include "fatal.hpp"
+
+namespace dena {
+
+const int opt_syslog = LOG_ERR | LOG_PID | LOG_CONS;
+
+void
+fatal_exit(const std::string& message)
+{
+ fprintf(stderr, "FATAL_EXIT: %s\n", message.c_str());
+ syslog(opt_syslog, "FATAL_EXIT: %s", message.c_str());
+ _exit(1);
+}
+
+void
+fatal_abort(const std::string& message)
+{
+ fprintf(stderr, "FATAL_COREDUMP: %s\n", message.c_str());
+ syslog(opt_syslog, "FATAL_COREDUMP: %s", message.c_str());
+ abort();
+}
+
+};
+
diff --git a/plugin/handler_socket/libhsclient/fatal.hpp b/plugin/handler_socket/libhsclient/fatal.hpp
new file mode 100644
index 00000000000..8a630fab1cb
--- /dev/null
+++ b/plugin/handler_socket/libhsclient/fatal.hpp
@@ -0,0 +1,22 @@
+
+// vim:sw=2:ai
+
+/*
+ * Copyright (C) 2010 DeNA Co.,Ltd.. All rights reserved.
+ * See COPYRIGHT.txt for details.
+ */
+
+#ifndef DENA_FATAL_HPP
+#define DENA_FATAL_HPP
+
+#include <string>
+
+namespace dena {
+
+void fatal_exit(const std::string& message);
+void fatal_abort(const std::string& message);
+
+};
+
+#endif
+
diff --git a/plugin/handler_socket/libhsclient/hstcpcli.cpp b/plugin/handler_socket/libhsclient/hstcpcli.cpp
new file mode 100644
index 00000000000..3753346aae7
--- /dev/null
+++ b/plugin/handler_socket/libhsclient/hstcpcli.cpp
@@ -0,0 +1,453 @@
+
+// vim:sw=2:ai
+
+/*
+ * Copyright (C) 2010 DeNA Co.,Ltd.. All rights reserved.
+ * See COPYRIGHT.txt for details.
+ */
+
+#include <stdexcept>
+
+#include "hstcpcli.hpp"
+#include "auto_file.hpp"
+#include "string_util.hpp"
+#include "auto_addrinfo.hpp"
+#include "escape.hpp"
+#include "util.hpp"
+
+/* TODO */
+#if !defined(__linux__) && !defined(__FreeBSD__) && !defined(MSG_NOSIGNAL)
+#define MSG_NOSIGNAL 0
+#endif
+
+#define DBG(x)
+
+namespace dena {
+
+struct hstcpcli : public hstcpcli_i, private noncopyable {
+ hstcpcli(const socket_args& args);
+ virtual void close();
+ virtual int reconnect();
+ virtual bool stable_point();
+ virtual void request_buf_open_index(size_t pst_id, const char *dbn,
+ const char *tbl, const char *idx, const char *retflds, const char *filflds);
+ #if 0
+ virtual void request_buf_find(size_t pst_id, const string_ref& op,
+ const string_ref *kvs, size_t kvslen, uint32_t limit, uint32_t skip);
+ virtual void request_buf_insert(size_t pst_id, const string_ref *fvs,
+ size_t fvslen);
+ virtual void request_buf_update(size_t pst_id, const string_ref& op,
+ const string_ref *kvs, size_t kvslen, uint32_t limit, uint32_t skip,
+ const string_ref *mvs, size_t mvslen);
+ virtual void request_buf_delete(size_t pst_id, const string_ref& op,
+ const string_ref *kvs, size_t kvslen, uint32_t limit, uint32_t skip);
+ #endif
+ virtual void request_buf_exec_generic(size_t pst_id, const string_ref& op,
+ const string_ref *kvs, size_t kvslen, uint32_t limit, uint32_t skip,
+ const string_ref& mod_op, const string_ref *mvs, size_t mvslen,
+ const hstcpcli_filter *fils, size_t filslen);
+ virtual int request_send();
+ virtual int response_recv(size_t& num_flds_r);
+ virtual const string_ref *get_next_row();
+ virtual void response_buf_remove();
+ virtual int get_error_code();
+ virtual std::string get_error();
+ private:
+ int read_more();
+ void clear_error();
+ int set_error(int code, const std::string& str);
+ private:
+ auto_file fd;
+ socket_args sargs;
+ string_buffer readbuf;
+ string_buffer writebuf;
+ size_t response_end_offset; /* incl newline */
+ size_t cur_row_offset;
+ size_t num_flds;
+ size_t num_req_bufd; /* buffered but not yet sent */
+ size_t num_req_sent; /* sent but not yet received */
+ size_t num_req_rcvd; /* received but not yet removed */
+ int error_code;
+ std::string error_str;
+ std::vector<string_ref> flds;
+};
+
+hstcpcli::hstcpcli(const socket_args& args)
+ : sargs(args), response_end_offset(0), cur_row_offset(0), num_flds(0),
+ num_req_bufd(0), num_req_sent(0), num_req_rcvd(0), error_code(0)
+{
+ std::string err;
+ if (socket_connect(fd, sargs, err) != 0) {
+ set_error(-1, err);
+ }
+}
+
+void
+hstcpcli::close()
+{
+ fd.close();
+ readbuf.clear();
+ writebuf.clear();
+ flds.clear();
+ response_end_offset = 0;
+ cur_row_offset = 0;
+ num_flds = 0;
+ num_req_bufd = 0;
+ num_req_sent = 0;
+ num_req_rcvd = 0;
+}
+
+int
+hstcpcli::reconnect()
+{
+ clear_error();
+ close();
+ std::string err;
+ if (socket_connect(fd, sargs, err) != 0) {
+ set_error(-1, err);
+ }
+ return error_code;
+}
+
+bool
+hstcpcli::stable_point()
+{
+ /* returns true if cli can send a new request */
+ return fd.get() >= 0 && num_req_bufd == 0 && num_req_sent == 0 &&
+ num_req_rcvd == 0 && response_end_offset == 0;
+}
+
+int
+hstcpcli::get_error_code()
+{
+ return error_code;
+}
+
+std::string
+hstcpcli::get_error()
+{
+ return error_str;
+}
+
+int
+hstcpcli::read_more()
+{
+ const size_t block_size = 4096; // FIXME
+ char *const wp = readbuf.make_space(block_size);
+ const ssize_t rlen = read(fd.get(), wp, block_size);
+ if (rlen <= 0) {
+ if (rlen < 0) {
+ error_str = "read: failed";
+ } else {
+ error_str = "read: eof";
+ }
+ return rlen;
+ }
+ readbuf.space_wrote(rlen);
+ return rlen;
+}
+
+void
+hstcpcli::clear_error()
+{
+ DBG(fprintf(stderr, "CLEAR_ERROR: %d\n", error_code));
+ error_code = 0;
+ error_str.clear();
+}
+
+int
+hstcpcli::set_error(int code, const std::string& str)
+{
+ DBG(fprintf(stderr, "SET_ERROR: %d\n", code));
+ error_code = code;
+ error_str = str;
+ return error_code;
+}
+
+void
+hstcpcli::request_buf_open_index(size_t pst_id, const char *dbn,
+ const char *tbl, const char *idx, const char *retflds, const char *filflds)
+{
+ if (num_req_sent > 0 || num_req_rcvd > 0) {
+ close();
+ set_error(-1, "request_buf_open_index: protocol out of sync");
+ return;
+ }
+ const string_ref dbn_ref(dbn, strlen(dbn));
+ const string_ref tbl_ref(tbl, strlen(tbl));
+ const string_ref idx_ref(idx, strlen(idx));
+ const string_ref rfs_ref(retflds, strlen(retflds));
+ writebuf.append_literal("P\t");
+ append_uint32(writebuf, pst_id); // FIXME size_t ?
+ writebuf.append_literal("\t");
+ writebuf.append(dbn_ref.begin(), dbn_ref.end());
+ writebuf.append_literal("\t");
+ writebuf.append(tbl_ref.begin(), tbl_ref.end());
+ writebuf.append_literal("\t");
+ writebuf.append(idx_ref.begin(), idx_ref.end());
+ writebuf.append_literal("\t");
+ writebuf.append(rfs_ref.begin(), rfs_ref.end());
+ if (filflds != 0) {
+ const string_ref fls_ref(filflds, strlen(filflds));
+ writebuf.append_literal("\t");
+ writebuf.append(fls_ref.begin(), fls_ref.end());
+ }
+ writebuf.append_literal("\n");
+ ++num_req_bufd;
+}
+
+namespace {
+
+void
+append_delim_value(string_buffer& buf, const char *start, const char *finish)
+{
+ if (start == 0) {
+ /* null */
+ const char t[] = "\t\0";
+ buf.append(t, t + 2);
+ } else {
+ /* non-null */
+ buf.append_literal("\t");
+ escape_string(buf, start, finish);
+ }
+}
+
+};
+
+void
+hstcpcli::request_buf_exec_generic(size_t pst_id, const string_ref& op,
+ const string_ref *kvs, size_t kvslen, uint32_t limit, uint32_t skip,
+ const string_ref& mod_op, const string_ref *mvs, size_t mvslen,
+ const hstcpcli_filter *fils, size_t filslen)
+{
+ if (num_req_sent > 0 || num_req_rcvd > 0) {
+ close();
+ set_error(-1, "request_buf_exec_generic: protocol out of sync");
+ return;
+ }
+ append_uint32(writebuf, pst_id); // FIXME size_t ?
+ writebuf.append_literal("\t");
+ writebuf.append(op.begin(), op.end());
+ writebuf.append_literal("\t");
+ append_uint32(writebuf, kvslen); // FIXME size_t ?
+ for (size_t i = 0; i < kvslen; ++i) {
+ const string_ref& kv = kvs[i];
+ append_delim_value(writebuf, kv.begin(), kv.end());
+ }
+ if (limit != 0 || skip != 0 || mod_op.size() != 0 || filslen != 0) {
+ writebuf.append_literal("\t");
+ append_uint32(writebuf, limit); // FIXME size_t ?
+ if (skip != 0 || mod_op.size() != 0 || filslen != 0) {
+ writebuf.append_literal("\t");
+ append_uint32(writebuf, skip); // FIXME size_t ?
+ }
+ for (size_t i = 0; i < filslen; ++i) {
+ const hstcpcli_filter& f = fils[i];
+ writebuf.append_literal("\t");
+ writebuf.append(f.filter_type.begin(), f.filter_type.end());
+ writebuf.append_literal("\t");
+ writebuf.append(f.op.begin(), f.op.end());
+ writebuf.append_literal("\t");
+ append_uint32(writebuf, f.ff_offset);
+ append_delim_value(writebuf, f.val.begin(), f.val.end());
+ }
+ if (mod_op.size() != 0) {
+ writebuf.append_literal("\t");
+ writebuf.append(mod_op.begin(), mod_op.end());
+ for (size_t i = 0; i < mvslen; ++i) {
+ const string_ref& mv = mvs[i];
+ append_delim_value(writebuf, mv.begin(), mv.end());
+ }
+ }
+ }
+ writebuf.append_literal("\n");
+ ++num_req_bufd;
+}
+
+#if 0
+void
+hstcpcli::request_buf_find(size_t pst_id, const string_ref& op,
+ const string_ref *kvs, size_t kvslen, uint32_t limit, uint32_t skip)
+{
+ return request_buf_exec_generic(pst_id, op, kvs, kvslen, limit, skip,
+ 0, 0, 0);
+}
+
+void
+hstcpcli::request_buf_insert(size_t pst_id, const string_ref *fvs,
+ size_t fvslen)
+{
+ const string_ref insert_op("+", 1);
+ return request_buf_exec_generic(pst_id, insert_op, fvs, fvslen,
+ 0, 0, string_ref(), 0, 0);
+}
+
+void
+hstcpcli::request_buf_update(size_t pst_id, const string_ref& op,
+ const string_ref *kvs, size_t kvslen, uint32_t limit, uint32_t skip,
+ const string_ref *mvs, size_t mvslen)
+{
+ const string_ref modop_update("U", 1);
+ return request_buf_exec_generic(pst_id, op, kvs, kvslen, limit, skip,
+ modop_update, mvs, mvslen);
+}
+
+void
+hstcpcli::request_buf_delete(size_t pst_id, const string_ref& op,
+ const string_ref *kvs, size_t kvslen, uint32_t limit, uint32_t skip)
+{
+ const string_ref modop_delete("D", 1);
+ return request_buf_exec_generic(pst_id, op, kvs, kvslen, limit, skip,
+ modop_delete, 0, 0);
+}
+#endif
+
+int
+hstcpcli::request_send()
+{
+ if (error_code < 0) {
+ return error_code;
+ }
+ clear_error();
+ if (fd.get() < 0) {
+ close();
+ return set_error(-1, "write: closed");
+ }
+ if (num_req_bufd == 0 || num_req_sent > 0 || num_req_rcvd > 0) {
+ close();
+ return set_error(-1, "request_send: protocol out of sync");
+ }
+ const size_t wrlen = writebuf.size();
+ const ssize_t r = send(fd.get(), writebuf.begin(), wrlen, MSG_NOSIGNAL);
+ if (r <= 0) {
+ close();
+ return set_error(-1, r < 0 ? "write: failed" : "write: eof");
+ }
+ writebuf.erase_front(r);
+ if (static_cast<size_t>(r) != wrlen) {
+ close();
+ return set_error(-1, "write: incomplete");
+ }
+ num_req_sent = num_req_bufd;
+ num_req_bufd = 0;
+ DBG(fprintf(stderr, "REQSEND 0\n"));
+ return 0;
+}
+
+int
+hstcpcli::response_recv(size_t& num_flds_r)
+{
+ if (error_code < 0) {
+ return error_code;
+ }
+ clear_error();
+ if (num_req_bufd > 0 || num_req_sent == 0 || num_req_rcvd > 0 ||
+ response_end_offset != 0) {
+ close();
+ return set_error(-1, "response_recv: protocol out of sync");
+ }
+ cur_row_offset = 0;
+ num_flds_r = num_flds = 0;
+ if (fd.get() < 0) {
+ return set_error(-1, "read: closed");
+ }
+ size_t offset = 0;
+ while (true) {
+ const char *const lbegin = readbuf.begin() + offset;
+ const char *const lend = readbuf.end();
+ const char *const nl = memchr_char(lbegin, '\n', lend - lbegin);
+ if (nl != 0) {
+ offset = (nl + 1) - readbuf.begin();
+ break;
+ }
+ if (read_more() <= 0) {
+ close();
+ return set_error(-1, "read: eof");
+ }
+ }
+ response_end_offset = offset;
+ --num_req_sent;
+ ++num_req_rcvd;
+ char *start = readbuf.begin();
+ char *const finish = start + response_end_offset - 1;
+ const size_t resp_code = read_ui32(start, finish);
+ skip_one(start, finish);
+ num_flds_r = num_flds = read_ui32(start, finish);
+ if (resp_code != 0) {
+ skip_one(start, finish);
+ char *const err_begin = start;
+ read_token(start, finish);
+ char *const err_end = start;
+ std::string e = std::string(err_begin, err_end - err_begin);
+ if (e.empty()) {
+ e = "unknown_error";
+ }
+ return set_error(resp_code, e);
+ }
+ cur_row_offset = start - readbuf.begin();
+ DBG(fprintf(stderr, "[%s] ro=%zu eol=%zu\n",
+ std::string(readbuf.begin(), readbuf.begin() + response_end_offset)
+ .c_str(),
+ cur_row_offset, response_end_offset));
+ DBG(fprintf(stderr, "RES 0\n"));
+ return 0;
+}
+
+const string_ref *
+hstcpcli::get_next_row()
+{
+ if (num_flds == 0) {
+ DBG(fprintf(stderr, "GNR NF 0\n"));
+ return 0;
+ }
+ if (flds.size() < num_flds) {
+ flds.resize(num_flds);
+ }
+ char *start = readbuf.begin() + cur_row_offset;
+ char *const finish = readbuf.begin() + response_end_offset - 1;
+ if (start >= finish) { /* start[0] == nl */
+ DBG(fprintf(stderr, "GNR FIN 0 %p %p\n", start, finish));
+ return 0;
+ }
+ for (size_t i = 0; i < num_flds; ++i) {
+ skip_one(start, finish);
+ char *const fld_begin = start;
+ read_token(start, finish);
+ char *const fld_end = start;
+ char *wp = fld_begin;
+ if (is_null_expression(fld_begin, fld_end)) {
+ /* null */
+ flds[i] = string_ref();
+ } else {
+ unescape_string(wp, fld_begin, fld_end); /* in-place */
+ flds[i] = string_ref(fld_begin, wp);
+ }
+ }
+ cur_row_offset = start - readbuf.begin();
+ return &flds[0];
+}
+
+void
+hstcpcli::response_buf_remove()
+{
+ if (response_end_offset == 0) {
+ close();
+ set_error(-1, "response_buf_remove: protocol out of sync");
+ return;
+ }
+ readbuf.erase_front(response_end_offset);
+ response_end_offset = 0;
+ --num_req_rcvd;
+ cur_row_offset = 0;
+ num_flds = 0;
+ flds.clear();
+}
+
+hstcpcli_ptr
+hstcpcli_i::create(const socket_args& args)
+{
+ return hstcpcli_ptr(new hstcpcli(args));
+}
+
+};
+
diff --git a/plugin/handler_socket/libhsclient/hstcpcli.hpp b/plugin/handler_socket/libhsclient/hstcpcli.hpp
new file mode 100644
index 00000000000..b852576d509
--- /dev/null
+++ b/plugin/handler_socket/libhsclient/hstcpcli.hpp
@@ -0,0 +1,59 @@
+
+// vim:sw=2:ai
+
+/*
+ * Copyright (C) 2010 DeNA Co.,Ltd.. All rights reserved.
+ * See COPYRIGHT.txt for details.
+ */
+
+#ifndef DENA_HSTCPCLI_HPP
+#define DENA_HSTCPCLI_HPP
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <string>
+#include <memory>
+
+#include "config.hpp"
+#include "socket.hpp"
+#include "string_ref.hpp"
+#include "string_buffer.hpp"
+
+namespace dena {
+
+struct hstcpcli_filter {
+ string_ref filter_type;
+ string_ref op;
+ size_t ff_offset;
+ string_ref val;
+ hstcpcli_filter() : ff_offset(0) { }
+};
+
+struct hstcpcli_i;
+typedef std::auto_ptr<hstcpcli_i> hstcpcli_ptr;
+
+struct hstcpcli_i {
+ virtual ~hstcpcli_i() { }
+ virtual void close() = 0;
+ virtual int reconnect() = 0;
+ virtual bool stable_point() = 0;
+ virtual void request_buf_open_index(size_t pst_id, const char *dbn,
+ const char *tbl, const char *idx, const char *retflds,
+ const char *filflds = 0) = 0;
+ virtual void request_buf_exec_generic(size_t pst_id, const string_ref& op,
+ const string_ref *kvs, size_t kvslen, uint32_t limit, uint32_t skip,
+ const string_ref& mod_op, const string_ref *mvs, size_t mvslen,
+ const hstcpcli_filter *fils = 0, size_t filslen = 0) = 0;
+ virtual int request_send() = 0;
+ virtual int response_recv(size_t& num_flds_r) = 0;
+ virtual const string_ref *get_next_row() = 0;
+ virtual void response_buf_remove() = 0;
+ virtual int get_error_code() = 0;
+ virtual std::string get_error() = 0;
+ static hstcpcli_ptr create(const socket_args& args);
+};
+
+};
+
+#endif
+
diff --git a/plugin/handler_socket/libhsclient/libhsclient.spec.template b/plugin/handler_socket/libhsclient/libhsclient.spec.template
new file mode 100644
index 00000000000..3e4dfe04c25
--- /dev/null
+++ b/plugin/handler_socket/libhsclient/libhsclient.spec.template
@@ -0,0 +1,39 @@
+Summary: handlersocket client library
+Name: libhsclient
+Version: HANDLERSOCKET_VERSION
+Release: 1%{?dist}
+Group: System Environment/Libraries
+License: BSD
+Source: libhsclient.tar.gz
+Packager: Akira Higuchi <higuchi dot akira at dena dot jp>
+BuildRoot: /var/tmp/%{name}-%{version}-root
+
+%description
+
+%prep
+%setup -n %{name}
+
+%define _use_internal_dependency_generator 0
+
+%build
+make -f Makefile.plain
+
+%install
+rm -rf $RPM_BUILD_ROOT
+mkdir -p $RPM_BUILD_ROOT/usr/include/handlersocket
+mkdir -p $RPM_BUILD_ROOT/%{_bindir}
+mkdir -p $RPM_BUILD_ROOT/%{_libdir}
+install -m 755 libhsclient.a $RPM_BUILD_ROOT/%{_libdir}
+install -m 644 *.hpp $RPM_BUILD_ROOT/usr/include/handlersocket/
+
+%post
+/sbin/ldconfig
+
+%postun
+/sbin/ldconfig
+
+%files
+%defattr(-, root, root)
+/usr/include/*
+%{_libdir}/*.a
+
diff --git a/plugin/handler_socket/libhsclient/mutex.hpp b/plugin/handler_socket/libhsclient/mutex.hpp
new file mode 100644
index 00000000000..9cef2757f21
--- /dev/null
+++ b/plugin/handler_socket/libhsclient/mutex.hpp
@@ -0,0 +1,51 @@
+
+// vim:sw=2:ai
+
+/*
+ * Copyright (C) 2010 DeNA Co.,Ltd.. All rights reserved.
+ * See COPYRIGHT.txt for details.
+ */
+
+#ifndef DENA_MUTEX_HPP
+#define DENA_MUTEX_HPP
+
+#include <pthread.h>
+#include <stdlib.h>
+
+#include "fatal.hpp"
+#include "util.hpp"
+
+namespace dena {
+
+struct condition;
+
+struct mutex : private noncopyable {
+ friend struct condition;
+ mutex() {
+ if (pthread_mutex_init(&mtx, 0) != 0) {
+ fatal_abort("pthread_mutex_init");
+ }
+ }
+ ~mutex() {
+ if (pthread_mutex_destroy(&mtx) != 0) {
+ fatal_abort("pthread_mutex_destroy");
+ }
+ }
+ void lock() const {
+ if (pthread_mutex_lock(&mtx) != 0) {
+ fatal_abort("pthread_mutex_lock");
+ }
+ }
+ void unlock() const {
+ if (pthread_mutex_unlock(&mtx) != 0) {
+ fatal_abort("pthread_mutex_unlock");
+ }
+ }
+ private:
+ mutable pthread_mutex_t mtx;
+};
+
+};
+
+#endif
+
diff --git a/plugin/handler_socket/libhsclient/socket.cpp b/plugin/handler_socket/libhsclient/socket.cpp
new file mode 100644
index 00000000000..5d84a7c4adf
--- /dev/null
+++ b/plugin/handler_socket/libhsclient/socket.cpp
@@ -0,0 +1,186 @@
+
+// vim:sw=2:ai
+
+/*
+ * Copyright (C) 2010 DeNA Co.,Ltd.. All rights reserved.
+ * See COPYRIGHT.txt for details.
+ */
+
+#include <stdexcept>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <sys/un.h>
+
+#include "socket.hpp"
+#include "string_util.hpp"
+#include "fatal.hpp"
+
+namespace dena {
+
+void
+ignore_sigpipe()
+{
+ if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) {
+ fatal_abort("SIGPIPE SIG_IGN");
+ }
+}
+
+void
+socket_args::set(const config& conf)
+{
+ timeout = conf.get_int("timeout", 600);
+ listen_backlog = conf.get_int("listen_backlog", 256);
+ std::string node = conf.get_str("host", "");
+ std::string port = conf.get_str("port", "");
+ if (!node.empty() || !port.empty()) {
+ if (family == AF_UNIX || node == "/") {
+ set_unix_domain(port.c_str());
+ } else {
+ const char *nd = node.empty() ? 0 : node.c_str();
+ if (resolve(nd, port.c_str()) != 0) {
+ fatal_exit("getaddrinfo failed: " + node + ":" + port);
+ }
+ }
+ }
+ sndbuf = conf.get_int("sndbuf", 0);
+ rcvbuf = conf.get_int("rcvbuf", 0);
+}
+
+void
+socket_args::set_unix_domain(const char *path)
+{
+ family = AF_UNIX;
+ addr = sockaddr_storage();
+ addrlen = sizeof(sockaddr_un);
+ sockaddr_un *const ap = reinterpret_cast<sockaddr_un *>(&addr);
+ ap->sun_family = AF_UNIX;
+ strncpy(ap->sun_path, path, sizeof(ap->sun_path) - 1);
+}
+
+int
+socket_args::resolve(const char *node, const char *service)
+{
+ const int flags = (node == 0) ? AI_PASSIVE : 0;
+ auto_addrinfo ai;
+ addr = sockaddr_storage();
+ addrlen = 0;
+ const int r = ai.resolve(node, service, flags, family, socktype, protocol);
+ if (r != 0) {
+ return r;
+ }
+ memcpy(&addr, ai.get()->ai_addr, ai.get()->ai_addrlen);
+ addrlen = ai.get()->ai_addrlen;
+ return 0;
+}
+
+int
+socket_set_options(auto_file& fd, const socket_args& args, std::string& err_r)
+{
+ if (args.timeout != 0 && !args.nonblocking) {
+ struct timeval tv;
+ tv.tv_sec = args.timeout;
+ tv.tv_usec = 0;
+ if (setsockopt(fd.get(), SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) != 0) {
+ return errno_string("setsockopt SO_RCVTIMEO", errno, err_r);
+ }
+ tv.tv_sec = args.timeout;
+ tv.tv_usec = 0;
+ if (setsockopt(fd.get(), SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)) != 0) {
+ return errno_string("setsockopt SO_RCVTIMEO", errno, err_r);
+ }
+ }
+ if (args.nonblocking && fcntl(fd.get(), F_SETFL, O_NONBLOCK) != 0) {
+ return errno_string("fcntl O_NONBLOCK", errno, err_r);
+ }
+ if (args.sndbuf != 0) {
+ const int v = args.sndbuf;
+ if (setsockopt(fd.get(), SOL_SOCKET, SO_SNDBUF, &v, sizeof(v)) != 0) {
+ return errno_string("setsockopt SO_SNDBUF", errno, err_r);
+ }
+ }
+ if (args.rcvbuf != 0) {
+ const int v = args.rcvbuf;
+ if (setsockopt(fd.get(), SOL_SOCKET, SO_RCVBUF, &v, sizeof(v)) != 0) {
+ return errno_string("setsockopt SO_RCVBUF", errno, err_r);
+ }
+ }
+ return 0;
+}
+
+int
+socket_open(auto_file& fd, const socket_args& args, std::string& err_r)
+{
+ fd.reset(socket(args.family, args.socktype, args.protocol));
+ if (fd.get() < 0) {
+ return errno_string("socket", errno, err_r);
+ }
+ return socket_set_options(fd, args, err_r);
+}
+
+int
+socket_connect(auto_file& fd, const socket_args& args, std::string& err_r)
+{
+ int r = 0;
+ if ((r = socket_open(fd, args, err_r)) != 0) {
+ return r;
+ }
+ if (connect(fd.get(), reinterpret_cast<const sockaddr *>(&args.addr),
+ args.addrlen) != 0) {
+ if (!args.nonblocking || errno != EINPROGRESS) {
+ return errno_string("connect", errno, err_r);
+ }
+ }
+ return 0;
+}
+
+int
+socket_bind(auto_file& fd, const socket_args& args, std::string& err_r)
+{
+ fd.reset(socket(args.family, args.socktype, args.protocol));
+ if (fd.get() < 0) {
+ return errno_string("socket", errno, err_r);
+ }
+ if (args.reuseaddr) {
+ if (args.family == AF_UNIX) {
+ const sockaddr_un *const ap =
+ reinterpret_cast<const sockaddr_un *>(&args.addr);
+ if (unlink(ap->sun_path) != 0 && errno != ENOENT) {
+ return errno_string("unlink uds", errno, err_r);
+ }
+ } else {
+ int v = 1;
+ if (setsockopt(fd.get(), SOL_SOCKET, SO_REUSEADDR, &v, sizeof(v)) != 0) {
+ return errno_string("setsockopt SO_REUSEADDR", errno, err_r);
+ }
+ }
+ }
+ if (bind(fd.get(), reinterpret_cast<const sockaddr *>(&args.addr),
+ args.addrlen) != 0) {
+ return errno_string("bind", errno, err_r);
+ }
+ if (listen(fd.get(), args.listen_backlog) != 0) {
+ return errno_string("listen", errno, err_r);
+ }
+ if (args.nonblocking && fcntl(fd.get(), F_SETFL, O_NONBLOCK) != 0) {
+ return errno_string("fcntl O_NONBLOCK", errno, err_r);
+ }
+ return 0;
+}
+
+int
+socket_accept(int listen_fd, auto_file& fd, const socket_args& args,
+ sockaddr_storage& addr_r, socklen_t& addrlen_r, std::string& err_r)
+{
+ fd.reset(accept(listen_fd, reinterpret_cast<sockaddr *>(&addr_r),
+ &addrlen_r));
+ if (fd.get() < 0) {
+ return errno_string("accept", errno, err_r);
+ }
+ return socket_set_options(fd, args, err_r);
+}
+
+};
+
diff --git a/plugin/handler_socket/libhsclient/socket.hpp b/plugin/handler_socket/libhsclient/socket.hpp
new file mode 100644
index 00000000000..3da6020e843
--- /dev/null
+++ b/plugin/handler_socket/libhsclient/socket.hpp
@@ -0,0 +1,51 @@
+
+// vim:sw=2:ai
+
+/*
+ * Copyright (C) 2010 DeNA Co.,Ltd.. All rights reserved.
+ * See COPYRIGHT.txt for details.
+ */
+
+#ifndef DENA_SOCKET_HPP
+#define DENA_SOCKET_HPP
+
+#include <string>
+
+#include "auto_addrinfo.hpp"
+#include "auto_file.hpp"
+#include "config.hpp"
+
+namespace dena {
+
+struct socket_args {
+ sockaddr_storage addr;
+ socklen_t addrlen;
+ int family;
+ int socktype;
+ int protocol;
+ int timeout;
+ int listen_backlog;
+ bool reuseaddr;
+ bool nonblocking;
+ bool use_epoll;
+ int sndbuf;
+ int rcvbuf;
+ socket_args() : addr(), addrlen(0), family(AF_INET), socktype(SOCK_STREAM),
+ protocol(0), timeout(600), listen_backlog(256),
+ reuseaddr(true), nonblocking(false), use_epoll(false),
+ sndbuf(0), rcvbuf(0) { }
+ void set(const config& conf);
+ void set_unix_domain(const char *path);
+ int resolve(const char *node, const char *service);
+};
+
+void ignore_sigpipe();
+int socket_bind(auto_file& fd, const socket_args& args, std::string& err_r);
+int socket_connect(auto_file& fd, const socket_args& args, std::string& err_r);
+int socket_accept(int listen_fd, auto_file& fd, const socket_args& args,
+ sockaddr_storage& addr_r, socklen_t& addrlen_r, std::string& err_r);
+
+};
+
+#endif
+
diff --git a/plugin/handler_socket/libhsclient/string_buffer.hpp b/plugin/handler_socket/libhsclient/string_buffer.hpp
new file mode 100644
index 00000000000..c013f45f54f
--- /dev/null
+++ b/plugin/handler_socket/libhsclient/string_buffer.hpp
@@ -0,0 +1,118 @@
+
+// vim:sw=2:ai
+
+/*
+ * Copyright (C) 2010 DeNA Co.,Ltd.. All rights reserved.
+ * See COPYRIGHT.txt for details.
+ */
+
+#ifndef DENA_STRING_BUFFER_HPP
+#define DENA_STRING_BUFFER_HPP
+
+#include <vector>
+#include <stdlib.h>
+#include <string.h>
+
+#include "util.hpp"
+#include "allocator.hpp"
+#include "fatal.hpp"
+
+namespace dena {
+
+struct string_buffer : private noncopyable {
+ string_buffer() : buffer(0), begin_offset(0), end_offset(0), alloc_size(0) { }
+ ~string_buffer() {
+ DENA_FREE(buffer);
+ }
+ const char *begin() const {
+ return buffer + begin_offset;
+ }
+ const char *end() const {
+ return buffer + end_offset;
+ }
+ char *begin() {
+ return buffer + begin_offset;
+ }
+ char *end() {
+ return buffer + end_offset;
+ }
+ size_t size() const {
+ return end_offset - begin_offset;
+ }
+ void clear() {
+ begin_offset = end_offset = 0;
+ }
+ void resize(size_t len) {
+ if (size() < len) {
+ reserve(len);
+ memset(buffer + end_offset, 0, len - size());
+ }
+ end_offset = begin_offset + len;
+ }
+ void reserve(size_t len) {
+ if (alloc_size >= begin_offset + len) {
+ return;
+ }
+ size_t asz = alloc_size;
+ while (asz < begin_offset + len) {
+ if (asz == 0) {
+ asz = 16;
+ }
+ const size_t asz_n = asz << 1;
+ if (asz_n < asz) {
+ fatal_abort("string_buffer::resize() overflow");
+ }
+ asz = asz_n;
+ }
+ void *const p = DENA_REALLOC(buffer, asz);
+ if (p == 0) {
+ fatal_abort("string_buffer::resize() realloc");
+ }
+ buffer = static_cast<char *>(p);
+ alloc_size = asz;
+ }
+ void erase_front(size_t len) {
+ if (len >= size()) {
+ clear();
+ } else {
+ begin_offset += len;
+ }
+ }
+ char *make_space(size_t len) {
+ reserve(size() + len);
+ return buffer + end_offset;
+ }
+ void space_wrote(size_t len) {
+ len = std::min(len, alloc_size - end_offset);
+ end_offset = std::min(end_offset + len, alloc_size);
+ }
+ template <size_t N>
+ void append_literal(const char (& str)[N]) {
+ append(str, str + N - 1);
+ }
+ void append(const char *start, const char *finish) {
+ const size_t len = finish - start;
+ reserve(size() + len);
+ memcpy(buffer + end_offset, start, len);
+ end_offset += len;
+ }
+ void append_2(const char *s1, const char *f1, const char *s2,
+ const char *f2) {
+ const size_t l1 = f1 - s1;
+ const size_t l2 = f2 - s2;
+ reserve(end_offset + l1 + l2);
+ memcpy(buffer + end_offset, s1, l1);
+ memcpy(buffer + end_offset + l1, s2, l2);
+ end_offset += l1 + l2;
+ }
+ private:
+ char *buffer;
+ size_t begin_offset;
+ size_t end_offset;
+ size_t alloc_size;
+};
+
+};
+
+#endif
+
diff --git a/plugin/handler_socket/libhsclient/string_ref.hpp b/plugin/handler_socket/libhsclient/string_ref.hpp
new file mode 100644
index 00000000000..c5f93065228
--- /dev/null
+++ b/plugin/handler_socket/libhsclient/string_ref.hpp
@@ -0,0 +1,63 @@
+
+// vim:sw=2:ai
+
+/*
+ * Copyright (C) 2010 DeNA Co.,Ltd.. All rights reserved.
+ * See COPYRIGHT.txt for details.
+ */
+
+#ifndef DENA_STRING_REF_HPP
+#define DENA_STRING_REF_HPP
+
+#include <vector>
+#include <string.h>
+
+namespace dena {
+
+struct string_wref {
+ typedef char value_type;
+ char *begin() const { return start; }
+ char *end() const { return start + length; }
+ size_t size() const { return length; }
+ private:
+ char *start;
+ size_t length;
+ public:
+ string_wref(char *s = 0, size_t len = 0) : start(s), length(len) { }
+};
+
+struct string_ref {
+ typedef const char value_type;
+ const char *begin() const { return start; }
+ const char *end() const { return start + length; }
+ size_t size() const { return length; }
+ private:
+ const char *start;
+ size_t length;
+ public:
+ string_ref(const char *s = 0, size_t len = 0) : start(s), length(len) { }
+ string_ref(const char *s, const char *f) : start(s), length(f - s) { }
+ string_ref(const string_wref& w) : start(w.begin()), length(w.size()) { }
+};
+
+template <size_t N> inline bool
+operator ==(const string_ref& x, const char (& y)[N]) {
+ return (x.size() == N - 1) && (::memcmp(x.begin(), y, N - 1) == 0);
+}
+
+inline bool
+operator ==(const string_ref& x, const string_ref& y) {
+ return (x.size() == y.size()) &&
+ (::memcmp(x.begin(), y.begin(), x.size()) == 0);
+}
+
+inline bool
+operator !=(const string_ref& x, const string_ref& y) {
+ return (x.size() != y.size()) ||
+ (::memcmp(x.begin(), y.begin(), x.size()) != 0);
+}
+
+};
+
+#endif
+
diff --git a/plugin/handler_socket/libhsclient/string_util.cpp b/plugin/handler_socket/libhsclient/string_util.cpp
new file mode 100644
index 00000000000..8ee6000f3d0
--- /dev/null
+++ b/plugin/handler_socket/libhsclient/string_util.cpp
@@ -0,0 +1,182 @@
+
+// vim:sw=2:ai
+
+/*
+ * Copyright (C) 2010 DeNA Co.,Ltd.. All rights reserved.
+ * See COPYRIGHT.txt for details.
+ */
+
+#include <errno.h>
+#include <stdio.h>
+
+#include "string_util.hpp"
+
+namespace dena {
+
+string_wref
+get_token(char *& wp, char *wp_end, char delim)
+{
+ char *const wp_begin = wp;
+ char *const p = memchr_char(wp_begin, delim, wp_end - wp_begin);
+ if (p == 0) {
+ wp = wp_end;
+ return string_wref(wp_begin, wp_end - wp_begin);
+ }
+ wp = p + 1;
+ return string_wref(wp_begin, p - wp_begin);
+}
+
+template <typename T> T
+atoi_tmpl_nocheck(const char *start, const char *finish)
+{
+ T v = 0;
+ for (; start != finish; ++start) {
+ const char c = *start;
+ if (c < '0' || c > '9') {
+ break;
+ }
+ v *= 10;
+ v += static_cast<T>(c - '0');
+ }
+ return v;
+}
+
+template <typename T> T
+atoi_signed_tmpl_nocheck(const char *start, const char *finish)
+{
+ T v = 0;
+ bool negative = false;
+ if (start != finish) {
+ if (start[0] == '-') {
+ ++start;
+ negative = true;
+ } else if (start[0] == '+') {
+ ++start;
+ }
+ }
+ for (; start != finish; ++start) {
+ const char c = *start;
+ if (c < '0' || c > '9') {
+ break;
+ }
+ v *= 10;
+ if (negative) {
+ v -= static_cast<T>(c - '0');
+ } else {
+ v += static_cast<T>(c - '0');
+ }
+ }
+ return v;
+}
+
+uint32_t
+atoi_uint32_nocheck(const char *start, const char *finish)
+{
+ return atoi_tmpl_nocheck<uint32_t>(start, finish);
+}
+
+long long
+atoll_nocheck(const char *start, const char *finish)
+{
+ return atoi_signed_tmpl_nocheck<long long>(start, finish);
+}
+
+void
+append_uint32(string_buffer& buf, uint32_t v)
+{
+ char *const wp = buf.make_space(64);
+ const int len = snprintf(wp, 64, "%lu", static_cast<unsigned long>(v));
+ if (len > 0) {
+ buf.space_wrote(len);
+ }
+}
+
+std::string
+to_stdstring(uint32_t v)
+{
+ char buf[64];
+ snprintf(buf, sizeof(buf), "%lu", static_cast<unsigned long>(v));
+ return std::string(buf);
+}
+
+int
+errno_string(const char *s, int en, std::string& err_r)
+{
+ char buf[64];
+ snprintf(buf, sizeof(buf), "%s: %d", s, en);
+ err_r = std::string(buf);
+ return en;
+}
+
+template <typename T> size_t
+split_tmpl_arr(char delim, const T& buf, T *parts, size_t parts_len)
+{
+ typedef typename T::value_type value_type;
+ size_t i = 0;
+ value_type *start = buf.begin();
+ value_type *const finish = buf.end();
+ for (i = 0; i < parts_len; ++i) {
+ value_type *const p = memchr_char(start, delim, finish - start);
+ if (p == 0) {
+ parts[i] = T(start, finish - start);
+ ++i;
+ break;
+ }
+ parts[i] = T(start, p - start);
+ start = p + 1;
+ }
+ const size_t r = i;
+ for (; i < parts_len; ++i) {
+ parts[i] = T();
+ }
+ return r;
+}
+
+size_t
+split(char delim, const string_ref& buf, string_ref *parts,
+ size_t parts_len)
+{
+ return split_tmpl_arr(delim, buf, parts, parts_len);
+}
+
+size_t
+split(char delim, const string_wref& buf, string_wref *parts,
+ size_t parts_len)
+{
+ return split_tmpl_arr(delim, buf, parts, parts_len);
+}
+
+template <typename T, typename V> size_t
+split_tmpl_vec(char delim, const T& buf, V& parts)
+{
+ typedef typename T::value_type value_type;
+ size_t i = 0;
+ value_type *start = buf.begin();
+ value_type *const finish = buf.end();
+ while (true) {
+ value_type *const p = memchr_char(start, delim, finish - start);
+ if (p == 0) {
+ parts.push_back(T(start, finish - start));
+ break;
+ }
+ parts.push_back(T(start, p - start));
+ start = p + 1;
+ }
+ const size_t r = i;
+ return r;
+}
+
+size_t
+split(char delim, const string_ref& buf, std::vector<string_ref>& parts_r)
+{
+ return split_tmpl_vec(delim, buf, parts_r);
+}
+
+size_t
+split(char delim, const string_wref& buf, std::vector<string_wref>& parts_r)
+{
+ return split_tmpl_vec(delim, buf, parts_r);
+}
+
+};
+
diff --git a/plugin/handler_socket/libhsclient/string_util.hpp b/plugin/handler_socket/libhsclient/string_util.hpp
new file mode 100644
index 00000000000..45cc5b00c87
--- /dev/null
+++ b/plugin/handler_socket/libhsclient/string_util.hpp
@@ -0,0 +1,53 @@
+
+// vim:sw=2:ai
+
+/*
+ * Copyright (C) 2010 DeNA Co.,Ltd.. All rights reserved.
+ * See COPYRIGHT.txt for details.
+ */
+
+#ifndef DENA_STRING_UTIL_HPP
+#define DENA_STRING_UTIL_HPP
+
+#include <string>
+#include <string.h>
+#include <stdint.h>
+
+#include "string_buffer.hpp"
+#include "string_ref.hpp"
+
+namespace dena {
+
+inline const char *
+memchr_char(const char *s, int c, size_t n)
+{
+ return static_cast<const char *>(memchr(s, c, n));
+}
+
+inline char *
+memchr_char(char *s, int c, size_t n)
+{
+ return static_cast<char *>(memchr(s, c, n));
+}
+
+string_wref get_token(char *& wp, char *wp_end, char delim);
+uint32_t atoi_uint32_nocheck(const char *start, const char *finish);
+std::string to_stdstring(uint32_t v);
+void append_uint32(string_buffer& buf, uint32_t v);
+long long atoll_nocheck(const char *start, const char *finish);
+
+int errno_string(const char *s, int en, std::string& err_r);
+
+size_t split(char delim, const string_ref& buf, string_ref *parts,
+ size_t parts_len);
+size_t split(char delim, const string_wref& buf, string_wref *parts,
+ size_t parts_len);
+size_t split(char delim, const string_ref& buf,
+ std::vector<string_ref>& parts_r);
+size_t split(char delim, const string_wref& buf,
+ std::vector<string_wref>& parts_r);
+
+};
+
+#endif
+
diff --git a/plugin/handler_socket/libhsclient/thread.hpp b/plugin/handler_socket/libhsclient/thread.hpp
new file mode 100644
index 00000000000..8a43655427f
--- /dev/null
+++ b/plugin/handler_socket/libhsclient/thread.hpp
@@ -0,0 +1,84 @@
+
+// vim:sw=2:ai
+
+/*
+ * Copyright (C) 2010 DeNA Co.,Ltd.. All rights reserved.
+ * See COPYRIGHT.txt for details.
+ */
+
+#ifndef DENA_THREAD_HPP
+#define DENA_THREAD_HPP
+
+#include <stdexcept>
+#include <pthread.h>
+
+#include "fatal.hpp"
+
+namespace dena {
+
+template <typename T>
+struct thread : private noncopyable {
+ template <typename Ta> thread(const Ta& arg, size_t stack_sz = 256 * 1024)
+ : obj(arg), thr(0), need_join(false), stack_size(stack_sz) { }
+ template <typename Ta0, typename Ta1> thread(const Ta0& a0,
+ volatile Ta1& a1, size_t stack_sz = 256 * 1024)
+ : obj(a0, a1), thr(0), need_join(false), stack_size(stack_sz) { }
+ ~thread() {
+ join();
+ }
+ void start() {
+ if (!start_nothrow()) {
+ fatal_abort("thread::start");
+ }
+ }
+ bool start_nothrow() {
+ if (need_join) {
+ return need_join; /* true */
+ }
+ void *const arg = this;
+ pthread_attr_t attr;
+ if (pthread_attr_init(&attr) != 0) {
+ fatal_abort("pthread_attr_init");
+ }
+ if (pthread_attr_setstacksize(&attr, stack_size) != 0) {
+ fatal_abort("pthread_attr_setstacksize");
+ }
+ const int r = pthread_create(&thr, &attr, thread_main, arg);
+ if (pthread_attr_destroy(&attr) != 0) {
+ fatal_abort("pthread_attr_destroy");
+ }
+ if (r != 0) {
+ return need_join; /* false */
+ }
+ need_join = true;
+ return need_join; /* true */
+ }
+ void join() {
+ if (!need_join) {
+ return;
+ }
+ int e = 0;
+ if ((e = pthread_join(thr, 0)) != 0) {
+ fatal_abort("pthread_join");
+ }
+ need_join = false;
+ }
+ T& operator *() { return obj; }
+ T *operator ->() { return &obj; }
+ private:
+ static void *thread_main(void *arg) {
+ thread *p = static_cast<thread *>(arg);
+ p->obj();
+ return 0;
+ }
+ private:
+ T obj;
+ pthread_t thr;
+ bool need_join;
+ size_t stack_size;
+};
+
+};
+
+#endif
+
diff --git a/plugin/handler_socket/libhsclient/util.hpp b/plugin/handler_socket/libhsclient/util.hpp
new file mode 100644
index 00000000000..93d78cc7dc0
--- /dev/null
+++ b/plugin/handler_socket/libhsclient/util.hpp
@@ -0,0 +1,25 @@
+
+// vim:sw=2:ai
+
+/*
+ * Copyright (C) 2010 DeNA Co.,Ltd.. All rights reserved.
+ * See COPYRIGHT.txt for details.
+ */
+
+#ifndef DENA_UTIL_HPP
+#define DENA_UTIL_HPP
+
+namespace dena {
+
+/* boost::noncopyable */
+struct noncopyable {
+ noncopyable() { }
+ private:
+ noncopyable(const noncopyable&);
+ noncopyable& operator =(const noncopyable&);
+};
+
+};
+
+#endif
+
diff --git a/plugin/handler_socket/misc/microbench-hs.log b/plugin/handler_socket/misc/microbench-hs.log
new file mode 100644
index 00000000000..9fffe12e3fd
--- /dev/null
+++ b/plugin/handler_socket/misc/microbench-hs.log
@@ -0,0 +1,130 @@
+[a@c54hdd libhsclient]$ ./hstest_hs.sh host=192.168.100.104 key_mask=1000000 num_threads=100 num=10000000 timelimit=10 dbname=hstest
+now: 1274127653 cntdiff: 265538 tdiff: 1.000996 rps: 265273.757409
+now: 1274127654 cntdiff: 265762 tdiff: 1.000995 rps: 265497.850684
+now: 1274127655 cntdiff: 265435 tdiff: 1.001010 rps: 265167.196749
+now: 1274127656 cntdiff: 265144 tdiff: 1.000994 rps: 264880.654203
+now: 1274127657 cntdiff: 265593 tdiff: 1.000995 rps: 265329.018659
+now: 1274127658 cntdiff: 264863 tdiff: 1.000996 rps: 264599.492138
+now: 1274127659 cntdiff: 265688 tdiff: 1.001008 rps: 265420.447231
+now: 1274127660 cntdiff: 265727 tdiff: 1.000999 rps: 265461.810594
+now: 1274127661 cntdiff: 265848 tdiff: 1.001010 rps: 265579.716809
+now: 1274127662 cntdiff: 265430 tdiff: 1.000992 rps: 265167.001723
+now: 1274127663 cntdiff: 266379 tdiff: 1.001008 rps: 266110.751381
+now: 1274127664 cntdiff: 266244 tdiff: 1.001003 rps: 265977.217679
+now: 1274127665 cntdiff: 265737 tdiff: 1.000996 rps: 265472.559379
+now: 1274127666 cntdiff: 265878 tdiff: 1.001003 rps: 265611.647683
+(1274127656.104648: 1328292, 1274127666.114649: 3985679), 265473.20173 qps
+
+
+*************************** 1. row ***************************
+ Type: InnoDB
+ Name:
+Status:
+=====================================
+100518 5:18:13 INNODB MONITOR OUTPUT
+=====================================
+Per second averages calculated from the last 5 seconds
+----------
+BACKGROUND THREAD
+----------
+srv_master_thread loops: 191 1_second, 190 sleeps, 18 10_second, 5 background, 5 flush
+srv_master_thread log flush and writes: 190
+----------
+SEMAPHORES
+----------
+OS WAIT ARRAY INFO: reservation count 53519, signal count 29547
+Mutex spin waits 3083488, rounds 5159906, OS waits 50700
+RW-shared spins 21, OS waits 16; RW-excl spins 1, OS waits 4
+Spin rounds per wait: 1.67 mutex, 30.00 RW-shared, 151.00 RW-excl
+------------
+TRANSACTIONS
+------------
+Trx id counter EDA36085
+Purge done for trx's n:o < EC1F94A7 undo n:o < 0
+History list length 20
+LIST OF TRANSACTIONS FOR EACH SESSION:
+---TRANSACTION 0, not started, process no 4533, OS thread id 1079281984
+MySQL thread id 11, query id 16 localhost root
+show engine innodb status
+---TRANSACTION ED9D5959, not started, process no 4533, OS thread id 1089849664
+MySQL thread id 7, query id 0 handlersocket: mode=rd, 0 conns, 0 active
+---TRANSACTION ED9D5956, not started, process no 4533, OS thread id 1238796608
+MySQL thread id 1, query id 0 handlersocket: mode=rd, 0 conns, 0 active
+---TRANSACTION EDA36084, not started, process no 4533, OS thread id 1255582016
+mysql tables in use 1, locked 1
+MySQL thread id 3, query id 0 handlersocket: mode=rd, 12 conns, 7 active
+---TRANSACTION EDA36080, not started, process no 4533, OS thread id 1247189312
+mysql tables in use 1, locked 1
+MySQL thread id 2, query id 0 handlersocket: mode=rd, 36 conns, 18 active
+---TRANSACTION EDA36082, ACTIVE 0 sec, process no 4533, OS thread id 1263974720 committing
+MySQL thread id 4, query id 0 handlersocket: mode=rd, 37 conns, 20 active
+Trx read view will not see trx with id >= EDA36083, sees < EDA3607D
+---TRANSACTION EDA3607D, ACTIVE 0 sec, process no 4533, OS thread id 1272367424, thread declared inside InnoDB 500
+mysql tables in use 1, locked 1
+MySQL thread id 5, query id 0 handlersocket: mode=rd, 15 conns, 9 active
+Trx read view will not see trx with id >= EDA3607E, sees < EDA36079
+--------
+FILE I/O
+--------
+I/O thread 0 state: waiting for i/o request (insert buffer thread)
+I/O thread 1 state: waiting for i/o request (log thread)
+I/O thread 2 state: waiting for i/o request (read thread)
+I/O thread 3 state: waiting for i/o request (read thread)
+I/O thread 4 state: waiting for i/o request (read thread)
+I/O thread 5 state: waiting for i/o request (read thread)
+I/O thread 6 state: waiting for i/o request (write thread)
+I/O thread 7 state: waiting for i/o request (write thread)
+I/O thread 8 state: waiting for i/o request (write thread)
+I/O thread 9 state: waiting for i/o request (write thread)
+Pending normal aio reads: 0, aio writes: 0,
+ ibuf aio reads: 0, log i/o's: 0, sync i/o's: 0
+Pending flushes (fsync) log: 0; buffer pool: 0
+71 OS file reads, 235 OS file writes, 235 OS fsyncs
+0.00 reads/s, 0 avg bytes/read, 1.00 writes/s, 1.00 fsyncs/s
+-------------------------------------
+INSERT BUFFER AND ADAPTIVE HASH INDEX
+-------------------------------------
+Ibuf: size 1, free list len 0, seg size 2,
+0 inserts, 0 merged recs, 0 merges
+Hash table size 12750011, node heap has 2 buffer(s)
+267203.76 hash searches/s, 0.00 non-hash searches/s
+---
+LOG
+---
+Log sequence number 147179727377
+Log flushed up to 147179726685
+Last checkpoint at 147179716475
+0 pending log writes, 0 pending chkp writes
+194 log i/o's done, 1.00 log i/o's/second
+----------------------
+BUFFER POOL AND MEMORY
+----------------------
+Total memory allocated 6587154432; in additional pool allocated 0
+Dictionary memory allocated 33640
+Buffer pool size 393216
+Free buffers 393154
+Database pages 60
+Old database pages 0
+Modified db pages 1
+Pending reads 0
+Pending writes: LRU 0, flush list 0, single page 0
+Pages made young 0, not young 0
+0.00 youngs/s, 0.00 non-youngs/s
+Pages read 60, created 0, written 23
+0.00 reads/s, 0.00 creates/s, 0.00 writes/s
+Buffer pool hit rate 1000 / 1000, young-making rate 0 / 1000 not 0 / 1000
+Pages read ahead 0.00/s, evicted without access 0.00/s
+LRU len: 60, unzip_LRU len: 0
+I/O sum[0]:cur[0], unzip sum[0]:cur[0]
+--------------
+ROW OPERATIONS
+--------------
+2 queries inside InnoDB, 0 queries in queue
+3 read views open inside InnoDB
+Main thread process no. 4533, id 1230403904, state: sleeping
+Number of rows inserted 0, updated 0, deleted 0, read 37653556
+0.00 inserts/s, 0.00 updates/s, 0.00 deletes/s, 266608.28 reads/s
+----------------------------
+END OF INNODB MONITOR OUTPUT
+============================
+
diff --git a/plugin/handler_socket/misc/microbench-my.log b/plugin/handler_socket/misc/microbench-my.log
new file mode 100644
index 00000000000..2477af8a791
--- /dev/null
+++ b/plugin/handler_socket/misc/microbench-my.log
@@ -0,0 +1,125 @@
+
+[a@c54hdd libhsclient]$ ./hstest_my.sh host=192.168.100.104 key_mask=1000000 num_threads=100 num=10000000 timelimit=10 dbname=hstest
+now: 1274128046 cntdiff: 63061 tdiff: 1.000999 rps: 62998.066579
+now: 1274128047 cntdiff: 61227 tdiff: 1.001013 rps: 61165.037337
+now: 1274128048 cntdiff: 61367 tdiff: 1.001029 rps: 61303.917375
+now: 1274128049 cntdiff: 61959 tdiff: 1.000962 rps: 61899.451554
+now: 1274128050 cntdiff: 62176 tdiff: 1.001006 rps: 62113.520756
+now: 1274128051 cntdiff: 61367 tdiff: 1.000998 rps: 61305.815559
+now: 1274128052 cntdiff: 61644 tdiff: 1.001015 rps: 61581.497988
+now: 1274128053 cntdiff: 60659 tdiff: 1.000984 rps: 60599.373036
+now: 1274128054 cntdiff: 59459 tdiff: 1.000996 rps: 59399.831067
+now: 1274128055 cntdiff: 62310 tdiff: 1.001011 rps: 62247.074757
+now: 1274128056 cntdiff: 61947 tdiff: 1.000991 rps: 61885.664744
+now: 1274128057 cntdiff: 60675 tdiff: 1.001006 rps: 60614.029076
+now: 1274128058 cntdiff: 60312 tdiff: 1.001001 rps: 60251.680861
+now: 1274128059 cntdiff: 60290 tdiff: 1.001004 rps: 60229.530717
+(1274128049.309634: 309654, 1274128059.319648: 920493), 61022.79143 qps
+
+*************************** 1. row ***************************
+ Type: InnoDB
+ Name:
+Status:
+=====================================
+100518 5:24:51 INNODB MONITOR OUTPUT
+=====================================
+Per second averages calculated from the last 5 seconds
+----------
+BACKGROUND THREAD
+----------
+srv_master_thread loops: 220 1_second, 219 sleeps, 21 10_second, 6 background, 6 flush
+srv_master_thread log flush and writes: 219
+----------
+SEMAPHORES
+----------
+OS WAIT ARRAY INFO: reservation count 56193, signal count 30826
+Mutex spin waits 3415153, rounds 5618661, OS waits 53251
+RW-shared spins 24, OS waits 17; RW-excl spins 1, OS waits 5
+Spin rounds per wait: 1.65 mutex, 30.00 RW-shared, 181.00 RW-excl
+------------
+TRANSACTIONS
+------------
+Trx id counter EDB514D6
+Purge done for trx's n:o < EC1F94A7 undo n:o < 0
+History list length 20
+LIST OF TRANSACTIONS FOR EACH SESSION:
+---TRANSACTION 0, not started, process no 4533, OS thread id 1306585408
+MySQL thread id 113, query id 920620 localhost root
+show engine innodb status
+---TRANSACTION EDA708BB, not started, process no 4533, OS thread id 1272367424
+MySQL thread id 5, query id 0 handlersocket: mode=rd, 0 conns, 0 active
+---TRANSACTION ED9D5959, not started, process no 4533, OS thread id 1089849664
+MySQL thread id 7, query id 0 handlersocket: mode=rd, 0 conns, 0 active
+---TRANSACTION ED9D5956, not started, process no 4533, OS thread id 1238796608
+MySQL thread id 1, query id 0 handlersocket: mode=rd, 0 conns, 0 active
+---TRANSACTION EDA708BD, not started, process no 4533, OS thread id 1255582016
+MySQL thread id 3, query id 0 handlersocket: mode=rd, 0 conns, 0 active
+---TRANSACTION EDA708BF, not started, process no 4533, OS thread id 1247189312
+MySQL thread id 2, query id 0 handlersocket: mode=rd, 0 conns, 0 active
+---TRANSACTION EDA708BE, not started, process no 4533, OS thread id 1263974720
+MySQL thread id 4, query id 0 handlersocket: mode=rd, 0 conns, 0 active
+--------
+FILE I/O
+--------
+I/O thread 0 state: waiting for i/o request (insert buffer thread)
+I/O thread 1 state: waiting for i/o request (log thread)
+I/O thread 2 state: waiting for i/o request (read thread)
+I/O thread 3 state: waiting for i/o request (read thread)
+I/O thread 4 state: waiting for i/o request (read thread)
+I/O thread 5 state: waiting for i/o request (read thread)
+I/O thread 6 state: waiting for i/o request (write thread)
+I/O thread 7 state: waiting for i/o request (write thread)
+I/O thread 8 state: waiting for i/o request (write thread)
+I/O thread 9 state: waiting for i/o request (write thread)
+Pending normal aio reads: 0, aio writes: 0,
+ ibuf aio reads: 0, log i/o's: 0, sync i/o's: 0
+Pending flushes (fsync) log: 0; buffer pool: 0
+71 OS file reads, 269 OS file writes, 269 OS fsyncs
+0.00 reads/s, 0 avg bytes/read, 2.40 writes/s, 2.40 fsyncs/s
+-------------------------------------
+INSERT BUFFER AND ADAPTIVE HASH INDEX
+-------------------------------------
+Ibuf: size 1, free list len 0, seg size 2,
+0 inserts, 0 merged recs, 0 merges
+Hash table size 12750011, node heap has 2 buffer(s)
+65739.45 hash searches/s, 0.00 non-hash searches/s
+---
+LOG
+---
+Log sequence number 147179774153
+Log flushed up to 147179771813
+Last checkpoint at 147179761899
+0 pending log writes, 0 pending chkp writes
+220 log i/o's done, 1.60 log i/o's/second
+----------------------
+BUFFER POOL AND MEMORY
+----------------------
+Total memory allocated 6587154432; in additional pool allocated 0
+Dictionary memory allocated 33640
+Buffer pool size 393216
+Free buffers 393154
+Database pages 60
+Old database pages 0
+Modified db pages 1
+Pending reads 0
+Pending writes: LRU 0, flush list 0, single page 0
+Pages made young 0, not young 0
+0.00 youngs/s, 0.00 non-youngs/s
+Pages read 60, created 0, written 27
+0.00 reads/s, 0.00 creates/s, 0.40 writes/s
+Buffer pool hit rate 1000 / 1000, young-making rate 0 / 1000 not 0 / 1000
+Pages read ahead 0.00/s, evicted without access 0.00/s
+LRU len: 60, unzip_LRU len: 0
+I/O sum[0]:cur[0], unzip sum[0]:cur[0]
+--------------
+ROW OPERATIONS
+--------------
+0 queries inside InnoDB, 0 queries in queue
+1 read views open inside InnoDB
+Main thread process no. 4533, id 1230403904, state: sleeping
+Number of rows inserted 0, updated 0, deleted 0, read 40071920
+0.00 inserts/s, 0.00 updates/s, 0.00 deletes/s, 66321.54 reads/s
+----------------------------
+END OF INNODB MONITOR OUTPUT
+============================
+
diff --git a/plugin/handler_socket/perl-Net-HandlerSocket/COPYRIGHT.txt b/plugin/handler_socket/perl-Net-HandlerSocket/COPYRIGHT.txt
new file mode 100644
index 00000000000..41dda1279e2
--- /dev/null
+++ b/plugin/handler_socket/perl-Net-HandlerSocket/COPYRIGHT.txt
@@ -0,0 +1,27 @@
+
+ Copyright (c) 2010 DeNA Co.,Ltd.
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of DeNA Co.,Ltd. nor the names of its contributors
+ may be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY DeNA Co.,Ltd. "AS IS" AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ EVENT SHALL DeNA Co.,Ltd. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
diff --git a/plugin/handler_socket/perl-Net-HandlerSocket/Changes b/plugin/handler_socket/perl-Net-HandlerSocket/Changes
new file mode 100644
index 00000000000..7ed1e0192d4
--- /dev/null
+++ b/plugin/handler_socket/perl-Net-HandlerSocket/Changes
@@ -0,0 +1,6 @@
+Revision history for Perl extension HandlerSocket.
+
+0.01 Wed Mar 31 11:50:23 2010
+ - original version; created by h2xs 1.23 with options
+ -A -n HandlerSocket
+
diff --git a/plugin/handler_socket/perl-Net-HandlerSocket/HandlerSocket.xs b/plugin/handler_socket/perl-Net-HandlerSocket/HandlerSocket.xs
new file mode 100644
index 00000000000..7d9ee4929ae
--- /dev/null
+++ b/plugin/handler_socket/perl-Net-HandlerSocket/HandlerSocket.xs
@@ -0,0 +1,577 @@
+
+// vim:ai:sw=2:ts=8
+
+/*
+ * Copyright (C) 2010 DeNA Co.,Ltd.. All rights reserved.
+ * See COPYRIGHT.txt for details.
+ */
+
+#include "EXTERN.h"
+#include "perl.h"
+#include "XSUB.h"
+
+#include "ppport.h"
+
+#include "hstcpcli.hpp"
+
+#define DBG(x)
+
+static SV *
+arr_get_entry(AV *av, I32 avmax, I32 idx)
+{
+ if (idx > avmax) {
+ DBG(fprintf(stderr, "arr_get_entry1 %d %d\n", avmax, idx));
+ return 0;
+ }
+ SV **const ev = av_fetch(av, idx, 0);
+ if (ev == 0) {
+ DBG(fprintf(stderr, "arr_get_entry2 %d %d\n", avmax, idx));
+ return 0;
+ }
+ return *ev;
+}
+
+static int
+arr_get_intval(AV *av, I32 avmax, I32 idx, int default_val = 0)
+{
+ SV *const e = arr_get_entry(av, avmax, idx);
+ if (e == 0) {
+ return default_val;
+ }
+ return SvIV(e);
+}
+
+static const char *
+sv_get_strval(SV *sv)
+{
+ if (sv == 0 || !SvPOK(sv)) {
+ DBG(fprintf(stderr, "sv_get_strval\n"));
+ return 0;
+ }
+ return SvPV_nolen(sv);
+}
+
+static const char *
+arr_get_strval(AV *av, I32 avmax, I32 idx)
+{
+ SV *const e = arr_get_entry(av, avmax, idx);
+ return sv_get_strval(e);
+}
+
+static AV *
+sv_get_arrval(SV *sv)
+{
+ if (sv == 0 || !SvROK(sv)) {
+ DBG(fprintf(stderr, "sv_get_arrval1\n"));
+ return 0;
+ }
+ SV *const svtarget = SvRV(sv);
+ if (svtarget == 0 || SvTYPE(svtarget) != SVt_PVAV) {
+ DBG(fprintf(stderr, "sv_get_arrval2\n"));
+ return 0;
+ }
+ return (AV *)svtarget;
+}
+
+static AV *
+arr_get_arrval(AV *av, I32 avmax, I32 idx)
+{
+ SV *const e = arr_get_entry(av, avmax, idx);
+ if (e == 0) {
+ DBG(fprintf(stderr, "arr_get_arrval1\n"));
+ return 0;
+ }
+ return sv_get_arrval(e);
+}
+
+static void
+hv_to_strmap(HV *hv, std::map<std::string, std::string>& m_r)
+{
+ if (hv == 0) {
+ return;
+ }
+ hv_iterinit(hv);
+ HE *hent = 0;
+ while ((hent = hv_iternext(hv)) != 0) {
+ I32 klen = 0;
+ char *const k = hv_iterkey(hent, &klen);
+ DBG(fprintf(stderr, "k=%s\n", k));
+ const std::string key(k, klen);
+ SV *const vsv = hv_iterval(hv, hent);
+ STRLEN vlen = 0;
+ char *const v = SvPV(vsv, vlen);
+ DBG(fprintf(stderr, "v=%s\n", v));
+ const std::string val(v, vlen);
+ m_r[key] = val;
+ }
+}
+
+static void
+strrefarr_push_back(std::vector<dena::string_ref>& a_r, SV *sv)
+{
+ if (sv == 0 || SvTYPE(sv) == SVt_NULL) {
+ DBG(fprintf(stderr, "strrefarr_push_back: null\n"));
+ return a_r.push_back(dena::string_ref());
+ }
+ STRLEN vlen = 0;
+ char *const v = SvPV(sv, vlen);
+ DBG(fprintf(stderr, "strrefarr_push_back: %s\n", v));
+ a_r.push_back(dena::string_ref(v, vlen));
+}
+
+static void
+av_to_strrefarr(AV *av, std::vector<dena::string_ref>& a_r)
+{
+ if (av == 0) {
+ return;
+ }
+ const I32 len = av_len(av) + 1;
+ for (I32 i = 0; i < len; ++i) {
+ SV **const ev = av_fetch(av, i, 0);
+ strrefarr_push_back(a_r, ev ? *ev : 0);
+ }
+}
+
+static dena::string_ref
+sv_get_string_ref(SV *sv)
+{
+ if (sv == 0) {
+ return dena::string_ref();
+ }
+ STRLEN vlen = 0;
+ char *const v = SvPV(sv, vlen);
+ return dena::string_ref(v, vlen);
+}
+
+static IV
+sv_get_iv(SV *sv)
+{
+ if (sv == 0 || !SvIOK(sv)) {
+ return 0;
+ }
+ return SvIV(sv);
+}
+
+static void
+av_to_filters(AV *av, std::vector<dena::hstcpcli_filter>& f_r)
+{
+ DBG(fprintf(stderr, "av_to_filters: %p\n", av));
+ if (av == 0) {
+ return;
+ }
+ const I32 len = av_len(av) + 1;
+ DBG(fprintf(stderr, "av_to_filters: len=%d\n", (int)len));
+ for (I32 i = 0; i < len; ++i) {
+ AV *const earr = arr_get_arrval(av, len, i);
+ if (earr == 0) {
+ continue;
+ }
+ const I32 earrlen = av_len(earr) + 1;
+ dena::hstcpcli_filter fe;
+ fe.filter_type = sv_get_string_ref(arr_get_entry(earr, earrlen, 0));
+ fe.op = sv_get_string_ref(arr_get_entry(earr, earrlen, 1));
+ fe.ff_offset = sv_get_iv(arr_get_entry(earr, earrlen, 2));
+ fe.val = sv_get_string_ref(arr_get_entry(earr, earrlen, 3));
+ f_r.push_back(fe);
+ DBG(fprintf(stderr, "av_to_filters: %s %s %d %s\n",
+ fe.filter_action.begin(), fe.filter_op.begin(), (int)fe.ff_offset,
+ fe.value.begin()));
+ }
+}
+
+static void
+set_process_verbose_level(const std::map<std::string, std::string>& m)
+{
+ std::map<std::string, std::string>::const_iterator iter = m.find("verbose");
+ if (iter != m.end()) {
+ dena::verbose_level = atoi(iter->second.c_str());
+ }
+}
+
+static AV *
+execute_internal(SV *obj, int id, const char *op, AV *keys, int limit,
+ int skip, const char *modop, AV *modvals, AV *filters)
+{
+ AV *retval = (AV *)&PL_sv_undef;
+ dena::hstcpcli_i *const ptr =
+ reinterpret_cast<dena::hstcpcli_i *>(SvIV(SvRV(obj)));
+ do {
+ std::vector<dena::string_ref> keyarr, mvarr;
+ std::vector<dena::hstcpcli_filter> farr;
+ av_to_strrefarr(keys, keyarr);
+ dena::string_ref modop_ref;
+ if (modop != 0) {
+ modop_ref = dena::string_ref(modop, strlen(modop));
+ av_to_strrefarr(modvals, mvarr);
+ }
+ if (filters != 0) {
+ av_to_filters(filters, farr);
+ }
+ ptr->request_buf_exec_generic(id, dena::string_ref(op, strlen(op)),
+ &keyarr[0], keyarr.size(), limit, skip, modop_ref, &mvarr[0],
+ mvarr.size(), &farr[0], farr.size());
+ AV *const av = newAV();
+ retval = av;
+ if (ptr->request_send() != 0) {
+ break;
+ }
+ size_t nflds = 0;
+ ptr->response_recv(nflds);
+ const int e = ptr->get_error_code();
+ DBG(fprintf(stderr, "e=%d nflds=%zu\n", e, nflds));
+ av_push(av, newSViv(e));
+ if (e != 0) {
+ const std::string s = ptr->get_error();
+ av_push(av, newSVpvn(s.data(), s.size()));
+ } else {
+ const dena::string_ref *row = 0;
+ while ((row = ptr->get_next_row()) != 0) {
+ DBG(fprintf(stderr, "row=%p\n", row));
+ for (size_t i = 0; i < nflds; ++i) {
+ const dena::string_ref& v = row[i];
+ DBG(fprintf(stderr, "FLD %zu v=%s vbegin=%p\n", i,
+ std::string(v.begin(), v.size())
+ .c_str(), v.begin()));
+ if (v.begin() != 0) {
+ SV *const e = newSVpvn(
+ v.begin(), v.size());
+ av_push(av, e);
+ } else {
+ av_push(av, &PL_sv_undef);
+ }
+ }
+ }
+ }
+ if (e >= 0) {
+ ptr->response_buf_remove();
+ }
+ } while (0);
+ return retval;
+}
+
+struct execute_arg {
+ int id;
+ const char *op;
+ AV *keys;
+ int limit;
+ int skip;
+ const char *modop;
+ AV *modvals;
+ AV *filters;
+ execute_arg() : id(0), op(0), keys(0), limit(0), skip(0), modop(0),
+ modvals(0), filters(0) { }
+};
+
+static AV *
+execute_multi_internal(SV *obj, const execute_arg *args, size_t num_args)
+{
+ dena::hstcpcli_i *const ptr =
+ reinterpret_cast<dena::hstcpcli_i *>(SvIV(SvRV(obj)));
+ /* appends multiple requests to the send buffer */
+ for (size_t i = 0; i < num_args; ++i) {
+ std::vector<dena::string_ref> keyarr, mvarr;
+ std::vector<dena::hstcpcli_filter> farr;
+ const execute_arg& arg = args[i];
+ av_to_strrefarr(arg.keys, keyarr);
+ dena::string_ref modop_ref;
+ if (arg.modop != 0) {
+ modop_ref = dena::string_ref(arg.modop, strlen(arg.modop));
+ av_to_strrefarr(arg.modvals, mvarr);
+ }
+ if (arg.filters != 0) {
+ av_to_filters(arg.filters, farr);
+ }
+ ptr->request_buf_exec_generic(arg.id,
+ dena::string_ref(arg.op, strlen(arg.op)), &keyarr[0], keyarr.size(),
+ arg.limit, arg.skip, modop_ref, &mvarr[0], mvarr.size(), &farr[0],
+ farr.size());
+ }
+ AV *const retval = newAV();
+ /* sends the requests */
+ if (ptr->request_send() < 0) {
+ /* IO error */
+ AV *const av_respent = newAV();
+ av_push(retval, newRV_noinc((SV *)av_respent));
+ av_push(av_respent, newSViv(ptr->get_error_code()));
+ const std::string& s = ptr->get_error();
+ av_push(av_respent, newSVpvn(s.data(), s.size()));
+ return retval; /* retval : [ [ err_code, err_message ] ] */
+ }
+ /* receives responses */
+ for (size_t i = 0; i < num_args; ++i) {
+ AV *const av_respent = newAV();
+ av_push(retval, newRV_noinc((SV *)av_respent));
+ size_t nflds = 0;
+ const int e = ptr->response_recv(nflds);
+ av_push(av_respent, newSViv(e));
+ if (e != 0) {
+ const std::string& s = ptr->get_error();
+ av_push(av_respent, newSVpvn(s.data(), s.size()));
+ } else {
+ const dena::string_ref *row = 0;
+ while ((row = ptr->get_next_row()) != 0) {
+ for (size_t i = 0; i < nflds; ++i) {
+ const dena::string_ref& v = row[i];
+ DBG(fprintf(stderr, "%zu %s\n", i,
+ std::string(v.begin(), v.size()).c_str()));
+ if (v.begin() != 0) {
+ av_push(av_respent, newSVpvn(v.begin(), v.size()));
+ } else {
+ /* null */
+ av_push(av_respent, &PL_sv_undef);
+ }
+ }
+ }
+ }
+ if (e >= 0) {
+ ptr->response_buf_remove();
+ }
+ if (e < 0) {
+ return retval;
+ }
+ }
+ return retval;
+}
+
+MODULE = Net::HandlerSocket PACKAGE = Net::HandlerSocket
+
+SV *
+new(klass, args)
+ char *klass
+ HV *args
+CODE:
+ RETVAL = &PL_sv_undef;
+ dena::config conf;
+ hv_to_strmap(args, conf);
+ set_process_verbose_level(conf);
+ dena::socket_args sargs;
+ sargs.set(conf);
+ dena::hstcpcli_ptr p = dena::hstcpcli_i::create(sargs);
+ SV *const objref = newSViv(0);
+ SV *const obj = newSVrv(objref, klass);
+ dena::hstcpcli_i *const ptr = p.get();
+ sv_setiv(obj, reinterpret_cast<IV>(ptr));
+ p.release();
+ SvREADONLY_on(obj);
+ RETVAL = objref;
+OUTPUT:
+ RETVAL
+
+void
+DESTROY(obj)
+ SV *obj
+CODE:
+ dena::hstcpcli_i *const ptr =
+ reinterpret_cast<dena::hstcpcli_i *>(SvIV(SvRV(obj)));
+ delete ptr;
+
+void
+close(obj)
+ SV *obj
+CODE:
+ dena::hstcpcli_i *const ptr =
+ reinterpret_cast<dena::hstcpcli_i *>(SvIV(SvRV(obj)));
+ ptr->close();
+
+int
+reconnect(obj)
+ SV *obj
+CODE:
+ RETVAL = 0;
+ dena::hstcpcli_i *const ptr =
+ reinterpret_cast<dena::hstcpcli_i *>(SvIV(SvRV(obj)));
+ RETVAL = ptr->reconnect();
+OUTPUT:
+ RETVAL
+
+int
+stable_point(obj)
+ SV *obj
+CODE:
+ RETVAL = 0;
+ dena::hstcpcli_i *const ptr =
+ reinterpret_cast<dena::hstcpcli_i *>(SvIV(SvRV(obj)));
+ const bool rv = ptr->stable_point();
+ RETVAL = static_cast<int>(rv);
+OUTPUT:
+ RETVAL
+
+int
+get_error_code(obj)
+ SV *obj
+CODE:
+ RETVAL = 0;
+ dena::hstcpcli_i *const ptr =
+ reinterpret_cast<dena::hstcpcli_i *>(SvIV(SvRV(obj)));
+ RETVAL = ptr->get_error_code();
+OUTPUT:
+ RETVAL
+
+SV *
+get_error(obj)
+ SV *obj
+CODE:
+ RETVAL = &PL_sv_undef;
+ dena::hstcpcli_i *const ptr =
+ reinterpret_cast<dena::hstcpcli_i *>(SvIV(SvRV(obj)));
+ const std::string s = ptr->get_error();
+ RETVAL = newSVpvn(s.data(), s.size());
+OUTPUT:
+ RETVAL
+
+int
+open_index(obj, id, db, table, index, fields, ffields = 0)
+ SV *obj
+ int id
+ const char *db
+ const char *table
+ const char *index
+ const char *fields
+ SV *ffields
+CODE:
+ const char *const ffields_str = sv_get_strval(ffields);
+ RETVAL = 0;
+ dena::hstcpcli_i *const ptr =
+ reinterpret_cast<dena::hstcpcli_i *>(SvIV(SvRV(obj)));
+ do {
+ ptr->request_buf_open_index(id, db, table, index, fields, ffields_str);
+ if (ptr->request_send() != 0) {
+ break;
+ }
+ size_t nflds = 0;
+ ptr->response_recv(nflds);
+ const int e = ptr->get_error_code();
+ DBG(fprintf(stderr, "errcode=%d\n", ptr->get_error_code()));
+ if (e >= 0) {
+ ptr->response_buf_remove();
+ }
+ DBG(fprintf(stderr, "errcode=%d\n", ptr->get_error_code()));
+ } while (0);
+ RETVAL = ptr->get_error_code();
+OUTPUT:
+ RETVAL
+
+AV *
+execute_single(obj, id, op, keys, limit, skip, mop = 0, mvs = 0, fils = 0)
+ SV *obj
+ int id
+ const char *op
+ AV *keys
+ int limit
+ int skip
+ SV *mop
+ SV *mvs
+ SV *fils
+CODE:
+ const char *const mop_str = sv_get_strval(mop);
+ AV *const mvs_av = sv_get_arrval(mvs);
+ AV *const fils_av = sv_get_arrval(fils);
+ RETVAL = execute_internal(obj, id, op, keys, limit, skip, mop_str, mvs_av,
+ fils_av);
+ sv_2mortal((SV *)RETVAL);
+OUTPUT:
+ RETVAL
+
+AV *
+execute_multi(obj, cmds)
+ SV *obj
+ AV *cmds
+CODE:
+ DBG(fprintf(stderr, "execute_multi0\n"));
+ const I32 cmdsmax = av_len(cmds);
+ execute_arg args[cmdsmax + 1]; /* GNU */
+ for (I32 i = 0; i <= cmdsmax; ++i) {
+ AV *const avtarget = arr_get_arrval(cmds, cmdsmax, i);
+ if (avtarget == 0) {
+ DBG(fprintf(stderr, "execute_multi1 %d\n", i));
+ continue;
+ }
+ const I32 argmax = av_len(avtarget);
+ if (argmax < 2) {
+ DBG(fprintf(stderr, "execute_multi2 %d\n", i));
+ continue;
+ }
+ execute_arg& ag = args[i];
+ ag.id = arr_get_intval(avtarget, argmax, 0);
+ ag.op = arr_get_strval(avtarget, argmax, 1);
+ ag.keys = arr_get_arrval(avtarget, argmax, 2);
+ ag.limit = arr_get_intval(avtarget, argmax, 3);
+ ag.skip = arr_get_intval(avtarget, argmax, 4);
+ ag.modop = arr_get_strval(avtarget, argmax, 5);
+ ag.modvals = arr_get_arrval(avtarget, argmax, 6);
+ ag.filters = arr_get_arrval(avtarget, argmax, 7);
+ DBG(fprintf(stderr, "execute_multi3 %d: %d %s %p %d %d %s %p %p\n",
+ i, ag.id, ag.op, ag.keys, ag.limit, ag.skip, ag.modop, ag.modvals,
+ ag.filters));
+ }
+ RETVAL = execute_multi_internal(obj, args, cmdsmax + 1);
+ sv_2mortal((SV *)RETVAL);
+OUTPUT:
+ RETVAL
+
+AV *
+execute_find(obj, id, op, keys, limit, skip, mop = 0, mvs = 0, fils = 0)
+ SV *obj
+ int id
+ const char *op
+ AV *keys
+ int limit
+ int skip
+ SV *mop
+ SV *mvs
+ SV *fils
+CODE:
+ const char *const mop_str = sv_get_strval(mop);
+ AV *const mvs_av = sv_get_arrval(mvs);
+ AV *const fils_av = sv_get_arrval(fils);
+ RETVAL = execute_internal(obj, id, op, keys, limit, skip, mop_str, mvs_av,
+ fils_av);
+ sv_2mortal((SV *)RETVAL);
+OUTPUT:
+ RETVAL
+
+AV *
+execute_update(obj, id, op, keys, limit, skip, modvals, fils = 0)
+ SV *obj
+ int id
+ const char *op
+ AV *keys
+ int limit
+ int skip
+ AV *modvals
+ SV *fils
+CODE:
+ AV *const fils_av = sv_get_arrval(fils);
+ RETVAL = execute_internal(obj, id, op, keys, limit, skip, "U",
+ modvals, fils_av);
+ sv_2mortal((SV *)RETVAL);
+OUTPUT:
+ RETVAL
+
+AV *
+execute_delete(obj, id, op, keys, limit, skip, fils = 0)
+ SV *obj
+ int id
+ const char *op
+ AV *keys
+ int limit
+ int skip
+ SV *fils
+CODE:
+ AV *const fils_av = sv_get_arrval(fils);
+ RETVAL = execute_internal(obj, id, op, keys, limit, skip, "D", 0, fils_av);
+ sv_2mortal((SV *)RETVAL);
+OUTPUT:
+ RETVAL
+
+AV *
+execute_insert(obj, id, fvals)
+ SV *obj
+ int id
+ AV *fvals
+CODE:
+ RETVAL = execute_internal(obj, id, "+", fvals, 0, 0, 0, 0, 0);
+ sv_2mortal((SV *)RETVAL);
+OUTPUT:
+ RETVAL
+
diff --git a/plugin/handler_socket/perl-Net-HandlerSocket/MANIFEST b/plugin/handler_socket/perl-Net-HandlerSocket/MANIFEST
new file mode 100644
index 00000000000..5df64094269
--- /dev/null
+++ b/plugin/handler_socket/perl-Net-HandlerSocket/MANIFEST
@@ -0,0 +1,8 @@
+Changes
+HandlerSocket.xs
+Makefile.PL
+MANIFEST
+ppport.h
+README
+t/HandlerSocket.t
+lib/HandlerSocket.pm
diff --git a/plugin/handler_socket/perl-Net-HandlerSocket/Makefile.PL b/plugin/handler_socket/perl-Net-HandlerSocket/Makefile.PL
new file mode 100644
index 00000000000..50c329db4b6
--- /dev/null
+++ b/plugin/handler_socket/perl-Net-HandlerSocket/Makefile.PL
@@ -0,0 +1,18 @@
+# use 5.010000;
+use ExtUtils::MakeMaker;
+# See lib/ExtUtils/MakeMaker.pm for details of how to influence
+# the contents of the Makefile that is written.
+WriteMakefile(
+ NAME => 'Net::HandlerSocket',
+ VERSION_FROM => 'lib/Net/HandlerSocket.pm', # finds $VERSION
+ PREREQ_PM => {}, # e.g., Module::Name => 1.1
+ ($] >= 5.005 ? ## Add these new keywords supported since 5.005
+ (ABSTRACT_FROM => 'lib/Net/HandlerSocket.pm', # retrieve abstract from module
+ AUTHOR => 'higuchi dot akira at dena dot jp>') : ()),
+ CC => 'g++ -fPIC',
+ LD => 'g++ -fPIC',
+ LIBS => ['-L../libhsclient -L../libhsclient/.libs -lhsclient'],
+ DEFINE => '',
+ INC => '-I. -I../libhsclient',
+ OPTIMIZE => '-g -O3 -Wall -Wno-unused',
+);
diff --git a/plugin/handler_socket/perl-Net-HandlerSocket/Makefile.PL.installed b/plugin/handler_socket/perl-Net-HandlerSocket/Makefile.PL.installed
new file mode 100644
index 00000000000..1b7649498d2
--- /dev/null
+++ b/plugin/handler_socket/perl-Net-HandlerSocket/Makefile.PL.installed
@@ -0,0 +1,20 @@
+# use 5.010000;
+use ExtUtils::MakeMaker;
+# See lib/ExtUtils/MakeMaker.pm for details of how to influence
+# the contents of the Makefile that is written.
+WriteMakefile(
+ NAME => 'Net::HandlerSocket',
+ VERSION_FROM => 'lib/Net/HandlerSocket.pm', # finds $VERSION
+ PREREQ_PM => {}, # e.g., Module::Name => 1.1
+ ($] >= 5.005 ? ## Add these new keywords supported since 5.005
+ (ABSTRACT_FROM => 'lib/Net/HandlerSocket.pm', # retrieve abstract from module
+ AUTHOR => 'higuchi dot akira at dena dot jp>') : ()),
+ CC => 'g++ -fPIC',
+ LD => 'g++ -fPIC',
+ LIBS => ['-lhsclient'], # e.g., '-lm'
+ DEFINE => '', # e.g., '-DHAVE_SOMETHING'
+ INC => '-I. -I/usr/include/handlersocket',
+ OPTIMIZE => '-g -O3 -Wall -Wno-unused',
+ # Un-comment this if you add C files to link with later:
+ # OBJECT => '$(O_FILES)', # link all the C files too
+);
diff --git a/plugin/handler_socket/perl-Net-HandlerSocket/README b/plugin/handler_socket/perl-Net-HandlerSocket/README
new file mode 100644
index 00000000000..b6aec952cdf
--- /dev/null
+++ b/plugin/handler_socket/perl-Net-HandlerSocket/README
@@ -0,0 +1,30 @@
+HandlerSocket version 0.01
+==========================
+
+The README is used to introduce the module and provide instructions on
+how to install the module, any machine dependencies it may have (for
+example C compilers and installed libraries) and any other information
+that should be provided before the module is installed.
+
+A README file is required for CPAN modules since CPAN extracts the
+README file from a module distribution so that people browsing the
+archive can use it get an idea of the modules uses. It is usually a
+good idea to provide version information here so that people can
+decide whether fixes for the module are worth downloading.
+
+INSTALLATION
+
+To install this module type the following:
+
+ perl Makefile.PL
+ make
+ make test
+ make install
+
+DEPENDENCIES
+
+COPYRIGHT AND LICENCE
+
+ Copyright (C) 2010 DeNA Co.,Ltd.. All rights reserved.
+ See COPYRIGHT.txt for details.
+
diff --git a/plugin/handler_socket/perl-Net-HandlerSocket/lib/Net/HandlerSocket.pm b/plugin/handler_socket/perl-Net-HandlerSocket/lib/Net/HandlerSocket.pm
new file mode 100644
index 00000000000..f1ad55564c5
--- /dev/null
+++ b/plugin/handler_socket/perl-Net-HandlerSocket/lib/Net/HandlerSocket.pm
@@ -0,0 +1,68 @@
+
+package Net::HandlerSocket;
+
+use strict;
+use warnings;
+
+require Exporter;
+
+our @ISA = qw(Exporter);
+
+# Items to export into callers namespace by default. Note: do not export
+# names by default without a very good reason. Use EXPORT_OK instead.
+# Do not simply export all your public functions/methods/constants.
+
+# This allows declaration use Net::HandlerSocket ':all';
+# If you do not need this, moving things directly into @EXPORT or @EXPORT_OK
+# will save memory.
+our %EXPORT_TAGS = ( 'all' => [ qw(
+
+) ] );
+
+our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
+
+our @EXPORT = qw(
+
+);
+
+our $VERSION = '0.01';
+
+require XSLoader;
+XSLoader::load('Net::HandlerSocket', $VERSION);
+
+# Preloaded methods go here.
+
+1;
+__END__
+# Below is stub documentation for your module. You'd better edit it!
+
+=head1 NAME
+
+Net::HandlerSocket - Perl extension for blah blah blah
+
+=head1 SYNOPSIS
+
+ use Net::HandlerSocket;
+ my $hsargs = { host => 'localhost', port => 9999 };
+ my $cli = new Net::HandlerSocket($hsargs);
+ $cli->open_index(1, 'testdb', 'testtable1', 'PRIMARY', 'foo,bar,baz');
+ $cli->open_index(2, 'testdb', 'testtable2', 'i2', 'hoge,fuga');
+ $cli->execute_find(1, '>=', [ 'aaa', 'bbb' ], 5, 100);
+ # select foo,bar,baz from testdb.testtable1
+ # where pk1 = 'aaa' and pk2 = 'bbb' order by pk1, pk2
+ # limit 100, 5
+
+=head1 DESCRIPTION
+
+Stub documentation for Net::HandlerSocket, created by h2xs.
+
+=head1 AUTHOR
+
+Akira HiguchiE<lt>higuchi dot akira at dena dot jpE<gt>
+
+=head1 COPYRIGHT AND LICENSE
+
+Copyright (C) 2010 DeNA Co.,Ltd.. All rights reserved.
+See COPYRIGHT.txt for details.
+
+=cut
diff --git a/plugin/handler_socket/perl-Net-HandlerSocket/lib/Net/HandlerSocket/Pool.pm b/plugin/handler_socket/perl-Net-HandlerSocket/lib/Net/HandlerSocket/Pool.pm
new file mode 100755
index 00000000000..c51fe60d591
--- /dev/null
+++ b/plugin/handler_socket/perl-Net-HandlerSocket/lib/Net/HandlerSocket/Pool.pm
@@ -0,0 +1,362 @@
+#!/usr/bin/perl
+
+package Net::HandlerSocket::HSPool;
+
+use strict;
+use warnings;
+use Net::HandlerSocket;
+use Socket;
+
+sub new {
+ my $self = {
+ config => $_[1],
+ reopen_interval => 60,
+ hostmap => { },
+ };
+ return bless $self, $_[0];
+}
+
+sub clear_pool {
+ my ($self) = @_;
+ $self->{hostmap} = { };
+}
+
+sub on_error {
+ my ($self, $obj) = @_;
+ my $error_func = $self->{config}->{error};
+ if (defined($error_func)) {
+ return &{$error_func}($obj);
+ }
+ die $obj;
+}
+
+sub on_warning {
+ my ($self, $obj) = @_;
+ my $warning_func = $self->{config}->{warning};
+ if (defined($warning_func)) {
+ return &{$warning_func}($obj);
+ }
+}
+
+sub get_conf {
+ my ($self, $dbtbl) = @_;
+ my $hcent = $self->{config}->{hostmap}->{$dbtbl};
+ if (!defined($hcent)) {
+ $self->on_error("get_conf: $dbtbl not found");
+ return undef;
+ }
+ my %cpy = %$hcent;
+ $cpy{port} ||= 9998;
+ $cpy{timeout} ||= 2;
+ return \%cpy;
+}
+
+sub resolve_hostname {
+ my ($self, $hcent, $host_ip_list) = @_;
+ if (defined($host_ip_list)) {
+ if (scalar(@$host_ip_list) > 0) {
+ $hcent->{host} = shift(@$host_ip_list);
+ return $host_ip_list;
+ }
+ return undef; # no more ip
+ }
+ my $host = $hcent->{host}; # unresolved name
+ $hcent->{hostname} = $host;
+ my $resolve_list_func = $self->{config}->{resolve_list};
+ if (defined($resolve_list_func)) {
+ $host_ip_list = &{$resolve_list_func}($host);
+ if (scalar(@$host_ip_list) > 0) {
+ $hcent->{host} = shift(@$host_ip_list);
+ return $host_ip_list;
+ }
+ return undef; # no more ip
+ }
+ my $resolve_func = $self->{config}->{resolve};
+ if (defined($resolve_func)) {
+ $hcent->{host} = &{$resolve_func}($host);
+ return [];
+ }
+ my $packed = gethostbyname($host);
+ if (!defined($packed)) {
+ return undef;
+ }
+ $hcent->{host} = inet_ntoa($packed);
+ return [];
+}
+
+sub get_handle_exec {
+ my ($self, $db, $tbl, $idx, $cols, $exec_multi, $exec_args) = @_;
+ my $now = time();
+ my $dbtbl = join('.', $db, $tbl);
+ my $hcent = $self->get_conf($dbtbl); # copy
+ if (!defined($hcent)) {
+ return undef;
+ }
+ my $hmkey = join(':', $hcent->{host}, $hcent->{port});
+ my $hment = $self->{hostmap}->{$hmkey};
+ # [ open_time, handle, index_map, host, next_index_id ]
+ my $host_ip_list;
+ TRY_OTHER_IP:
+ if (!defined($hment) ||
+ $hment->[0] + $self->{reopen_interval} < $now ||
+ !$hment->[1]->stable_point()) {
+ $host_ip_list = $self->resolve_hostname($hcent, $host_ip_list);
+ if (!defined($host_ip_list)) {
+ my $hostport = $hmkey . '(' . $hcent->{host} . ')';
+ $self->on_error("HSPool::get_handle" .
+ "($db, $tbl, $idx, $cols): host=$hmkey: " .
+ "no more active ip");
+ return undef;
+ }
+ my $hnd = new Net::HandlerSocket($hcent);
+ my %m = ();
+ $hment = [ $now, $hnd, \%m, $hcent->{host}, 1 ];
+ $self->{hostmap}->{$hmkey} = $hment;
+ }
+ my $hnd = $hment->[1];
+ my $idxmap = $hment->[2];
+ my $imkey = join(':', $idx, $cols);
+ my $idx_id = $idxmap->{$imkey};
+ if (!defined($idx_id)) {
+ $idx_id = $hment->[4];
+ my $e = $hnd->open_index($idx_id, $db, $tbl, $idx, $cols);
+ if ($e != 0) {
+ my $estr = $hnd->get_error();
+ my $hostport = $hmkey . '(' . $hcent->{host} . ')';
+ my $errmess = "HSPool::get_handle open_index" .
+ "($db, $tbl, $idx, $cols): host=$hostport " .
+ "err=$e($estr)";
+ $self->on_warning($errmess);
+ $hnd->close();
+ $hment = undef;
+ goto TRY_OTHER_IP;
+ }
+ $hment->[4]++;
+ $idxmap->{$imkey} = $idx_id;
+ }
+ if ($exec_multi) {
+ my $resarr;
+ for my $cmdent (@$exec_args) {
+ $cmdent->[0] = $idx_id;
+ }
+ if (scalar(@$exec_args) == 0) {
+ $resarr = [];
+ } else {
+ $resarr = $hnd->execute_multi($exec_args);
+ }
+ my $i = 0;
+ for my $res (@$resarr) {
+ if ($res->[0] != 0) {
+ my $cmdent = $exec_args->[$i];
+ my $ec = $res->[0];
+ my $estr = $res->[1];
+ my $op = $cmdent->[1];
+ my $kfvs = $cmdent->[2];
+ my $kvstr = defined($kfvs)
+ ? join(',', @$kfvs) : '';
+ my $limit = $cmdent->[3] || 0;
+ my $skip = $cmdent->[4] || 0;
+ my $hostport = $hmkey . '(' . $hcent->{host}
+ . ')';
+ my $errmess = "HSPool::get_handle execm" .
+ "($db, $tbl, $idx, [$cols], " .
+ "($idx_id), $op, [$kvstr] " .
+ "$limit, $skip): " .
+ "host=$hostport err=$ec($estr)";
+ if ($res->[0] < 0 || $res->[0] == 2) {
+ $self->on_warning($errmess);
+ $hnd->close();
+ $hment = undef;
+ goto TRY_OTHER_IP;
+ } else {
+ $self->on_error($errmess);
+ }
+ }
+ shift(@$res);
+ ++$i;
+ }
+ return $resarr;
+ } else {
+ my $res = $hnd->execute_find($idx_id, @$exec_args);
+ if ($res->[0] != 0) {
+ my ($op, $kfvals, $limit, $skip) = @$exec_args;
+ my $ec = $res->[0];
+ my $estr = $res->[1];
+ my $kvstr = join(',', @$kfvals);
+ my $hostport = $hmkey . '(' . $hcent->{host} . ')';
+ my $errmess = "HSPool::get_handle exec" .
+ "($db, $tbl, $idx, [$cols], ($idx_id), " .
+ "$op, [$kvstr], $limit, $skip): " .
+ "host=$hostport err=$ec($estr)";
+ if ($res->[0] < 0 || $res->[0] == 2) {
+ $self->on_warning($errmess);
+ $hnd->close();
+ $hment = undef;
+ goto TRY_OTHER_IP;
+ } else {
+ $self->on_error($errmess);
+ }
+ }
+ shift(@$res);
+ return $res;
+ }
+}
+
+sub index_find {
+ my ($self, $db, $tbl, $idx, $cols, $op, $kfvals, $limit, $skip) = @_;
+ # cols: comma separated list
+ # kfvals: arrayref
+ $limit ||= 0;
+ $skip ||= 0;
+ my $res = $self->get_handle_exec($db, $tbl, $idx, $cols,
+ 0, [ $op, $kfvals, $limit, $skip ]);
+ return $res;
+}
+
+sub index_find_multi {
+ my ($self, $db, $tbl, $idx, $cols, $cmdlist) = @_;
+ # cols : comma separated list
+ # cmdlist : [ dummy, op, kfvals, limit, skip ]
+ # kfvals : arrayref
+ my $resarr = $self->get_handle_exec($db, $tbl, $idx, $cols,
+ 1, $cmdlist);
+ return $resarr;
+}
+
+sub result_single_to_arrarr {
+ my ($numcols, $hsres, $ret) = @_;
+ my $hsreslen = scalar(@$hsres);
+ my $rlen = int($hsreslen / $numcols);
+ $ret = [ ] if !defined($ret);
+ my @r = ();
+ my $p = 0;
+ for (my $i = 0; $i < $rlen; ++$i) {
+ my @a = splice(@$hsres, $p, $numcols);
+ $p += $numcols;
+ push(@$ret, \@a);
+ }
+ return $ret; # arrayref of arrayrefs
+}
+
+sub result_multi_to_arrarr {
+ my ($numcols, $mhsres, $ret) = @_;
+ $ret = [ ] if !defined($ret);
+ for my $hsres (@$mhsres) {
+ my $hsreslen = scalar(@$hsres);
+ my $rlen = int($hsreslen / $numcols);
+ my $p = 0;
+ for (my $i = 0; $i < $rlen; ++$i) {
+ my @a = splice(@$hsres, $p, $numcols);
+ $p += $numcols;
+ push(@$ret, \@a);
+ }
+ }
+ return $ret; # arrayref of arrayrefs
+}
+
+sub result_single_to_hasharr {
+ my ($names, $hsres, $ret) = @_;
+ my $nameslen = scalar(@$names);
+ my $hsreslen = scalar(@$hsres);
+ my $rlen = int($hsreslen / $nameslen);
+ $ret = [ ] if !defined($ret);
+ my $p = 0;
+ for (my $i = 0; $i < $rlen; ++$i) {
+ my %h = ();
+ for (my $j = 0; $j < $nameslen; ++$j, ++$p) {
+ $h{$names->[$j]} = $hsres->[$p];
+ }
+ push(@$ret, \%h);
+ }
+ return $ret; # arrayref of hashrefs
+}
+
+sub result_multi_to_hasharr {
+ my ($names, $mhsres, $ret) = @_;
+ my $nameslen = scalar(@$names);
+ $ret = [ ] if !defined($ret);
+ for my $hsres (@$mhsres) {
+ my $hsreslen = scalar(@$hsres);
+ my $rlen = int($hsreslen / $nameslen);
+ my $p = 0;
+ for (my $i = 0; $i < $rlen; ++$i) {
+ my %h = ();
+ for (my $j = 0; $j < $nameslen; ++$j, ++$p) {
+ $h{$names->[$j]} = $hsres->[$p];
+ }
+ push(@$ret, \%h);
+ }
+ }
+ return $ret; # arrayref of hashrefs
+}
+
+sub result_single_to_hashhash {
+ my ($names, $key, $hsres, $ret) = @_;
+ my $nameslen = scalar(@$names);
+ my $hsreslen = scalar(@$hsres);
+ my $rlen = int($hsreslen / $nameslen);
+ $ret = { } if !defined($ret);
+ my $p = 0;
+ for (my $i = 0; $i < $rlen; ++$i) {
+ my %h = ();
+ for (my $j = 0; $j < $nameslen; ++$j, ++$p) {
+ $h{$names->[$j]} = $hsres->[$p];
+ }
+ my $k = $h{$key};
+ $ret->{$k} = \%h if defined($k);
+ }
+ return $ret; # hashref of hashrefs
+}
+
+sub result_multi_to_hashhash {
+ my ($names, $key, $mhsres, $ret) = @_;
+ my $nameslen = scalar(@$names);
+ $ret = { } if !defined($ret);
+ for my $hsres (@$mhsres) {
+ my $hsreslen = scalar(@$hsres);
+ my $rlen = int($hsreslen / $nameslen);
+ my $p = 0;
+ for (my $i = 0; $i < $rlen; ++$i) {
+ my %h = ();
+ for (my $j = 0; $j < $nameslen; ++$j, ++$p) {
+ $h{$names->[$j]} = $hsres->[$p];
+ }
+ my $k = $h{$key};
+ $ret->{$k} = \%h if defined($k);
+ }
+ }
+ return $ret; # hashref of hashrefs
+}
+
+sub select_cols_where_eq_aa {
+ # SELECT $cols FROM $db.$tbl WHERE $idx_key = $kv LIMIT 1
+ my ($self, $db, $tbl, $idx, $cols_aref, $kv_aref) = @_;
+ my $cols_str = join(',', @$cols_aref);
+ my $res = $self->index_find($db, $tbl, $idx, $cols_str, '=', $kv_aref);
+ return result_single_to_arrarr(scalar(@$cols_aref), $res);
+}
+
+sub select_cols_where_eq_hh {
+ # SELECT $cols FROM $db.$tbl WHERE $idx_key = $kv LIMIT 1
+ my ($self, $db, $tbl, $idx, $cols_aref, $kv_aref, $retkey) = @_;
+ my $cols_str = join(',', @$cols_aref);
+ my $res = $self->index_find($db, $tbl, $idx, $cols_str, '=', $kv_aref);
+ my $r = result_single_to_hashhash($cols_aref, $retkey, $res);
+ return $r;
+}
+
+sub select_cols_where_in_hh {
+ # SELECT $cols FROM $db.$tbl WHERE $idx_key in ($vals)
+ my ($self, $db, $tbl, $idx, $cols_aref, $vals_aref, $retkey) = @_;
+ my $cols_str = join(',', @$cols_aref);
+ my @cmdlist = ();
+ for my $v (@$vals_aref) {
+ push(@cmdlist, [ -1, '=', [ $v ] ]);
+ }
+ my $res = $self->index_find_multi($db, $tbl, $idx, $cols_str,
+ \@cmdlist);
+ return result_multi_to_hashhash($cols_aref, $retkey, $res);
+}
+
+1;
+
diff --git a/plugin/handler_socket/perl-Net-HandlerSocket/perl-Net-HandlerSocket.spec.template b/plugin/handler_socket/perl-Net-HandlerSocket/perl-Net-HandlerSocket.spec.template
new file mode 100644
index 00000000000..304baf03ffb
--- /dev/null
+++ b/plugin/handler_socket/perl-Net-HandlerSocket/perl-Net-HandlerSocket.spec.template
@@ -0,0 +1,127 @@
+#
+# - HandlerSocket -
+# This spec file was automatically generated by cpan2rpm [ver: 2.027]
+# The following arguments were used:
+# --no-sign perl-Net-HandlerSocket.tar.gz
+# For more information on cpan2rpm please visit: http://perl.arix.com/
+#
+
+%define pkgname perl-Net-HandlerSocket
+%define filelist %{pkgname}-%{version}-filelist
+%define NVR %{pkgname}-%{version}-%{release}
+%define maketest 1
+
+name: perl-Net-HandlerSocket
+summary: HandlerSocket - Perl extension for handlersocket
+version: HANDLERSOCKET_VERSION
+release: 1%{?dist}
+packager: Akira Higuchi <higuchi dot akira at dena dot jp>
+license: BSD
+group: Applications/CPAN
+group: System Environment/Libraries
+buildroot: %{_tmppath}/%{name}-%{version}-%(id -u -n)
+prefix: %(echo %{_prefix})
+source: perl-Net-HandlerSocket.tar.gz
+BuildRequires: libhsclient
+Requires: libhsclient
+Obsoletes: perl-DB-HandlerSocket
+
+%description
+Stub documentation for HandlerSocket, created by h2xs. It looks like the
+author of the extension was negligent enough to leave the stub
+unedited.
+
+#
+# This package was generated automatically with the cpan2rpm
+# utility. To get this software or for more information
+# please visit: http://perl.arix.com/
+#
+
+%prep
+%setup -q -n %{pkgname}
+chmod -R u+w %{_builddir}/%{pkgname}
+
+%build
+grep -rsl '^#!.*perl' . |
+grep -v '.bak$' |xargs --no-run-if-empty \
+%__perl -MExtUtils::MakeMaker -e 'MY->fixin(@ARGV)'
+CFLAGS="$RPM_OPT_FLAGS"
+%{__perl} Makefile.PL.installed `%{__perl} -MExtUtils::MakeMaker -e ' print qq|PREFIX=%{buildroot}%{_prefix}| if \$ExtUtils::MakeMaker::VERSION =~ /5\.9[1-6]|6\.0[0-5]/ '`
+%{__make}
+%if %maketest
+%{__make} test
+%endif
+
+%install
+[ "%{buildroot}" != "/" ] && rm -rf %{buildroot}
+
+%{makeinstall} `%{__perl} -MExtUtils::MakeMaker -e ' print \$ExtUtils::MakeMaker::VERSION <= 6.05 ? qq|PREFIX=%{buildroot}%{_prefix}| : qq|DESTDIR=%{buildroot}| '`
+
+cmd=/usr/share/spec-helper/compress_files
+[ -x $cmd ] || cmd=/usr/lib/rpm/brp-compress
+[ -x $cmd ] && $cmd
+
+# SuSE Linux
+if [ -e /etc/SuSE-release -o -e /etc/UnitedLinux-release ]
+then
+ %{__mkdir_p} %{buildroot}/var/adm/perl-modules
+ %{__cat} `find %{buildroot} -name "perllocal.pod"` \
+ | %{__sed} -e s+%{buildroot}++g \
+ > %{buildroot}/var/adm/perl-modules/%{name}
+fi
+
+# remove special files
+find %{buildroot} -name "perllocal.pod" \
+ -o -name ".packlist" \
+ -o -name "*.bs" \
+ |xargs -i rm -f {}
+
+# no empty directories
+find %{buildroot}%{_prefix} \
+ -type d -depth \
+ -exec rmdir {} \; 2>/dev/null
+
+%{__perl} -MFile::Find -le '
+ find({ wanted => \&wanted, no_chdir => 1}, "%{buildroot}");
+ print "%doc Changes README";
+ for my $x (sort @dirs, @files) {
+ push @ret, $x unless indirs($x);
+ }
+ print join "\n", sort @ret;
+
+ sub wanted {
+ return if /auto$/;
+
+ local $_ = $File::Find::name;
+ my $f = $_; s|^\Q%{buildroot}\E||;
+ return unless length;
+ return $files[@files] = $_ if -f $f;
+
+ $d = $_;
+ /\Q$d\E/ && return for reverse sort @INC;
+ $d =~ /\Q$_\E/ && return
+ for qw|/etc %_prefix/man %_prefix/bin %_prefix/share|;
+
+ $dirs[@dirs] = $_;
+ }
+
+ sub indirs {
+ my $x = shift;
+ $x =~ /^\Q$_\E\// && $x ne $_ && return 1 for @dirs;
+ }
+ ' > %filelist
+
+[ -z %filelist ] && {
+ echo "ERROR: empty %files listing"
+ exit -1
+ }
+
+%clean
+[ "%{buildroot}" != "/" ] && rm -rf %{buildroot}
+
+%files -f %filelist
+%defattr(-,root,root)
+
+%changelog
+* Thu Apr 1 2010 a@localhost.localdomain
+- Initial build.
diff --git a/plugin/handler_socket/perl-Net-HandlerSocket/ppport.h b/plugin/handler_socket/perl-Net-HandlerSocket/ppport.h
new file mode 100644
index 00000000000..6e562f5b4a8
--- /dev/null
+++ b/plugin/handler_socket/perl-Net-HandlerSocket/ppport.h
@@ -0,0 +1,6375 @@
+#if 0
+<<'SKIP';
+#endif
+/*
+----------------------------------------------------------------------
+
+ ppport.h -- Perl/Pollution/Portability Version 3.13
+
+ Automatically created by Devel::PPPort running under perl 5.010000.
+
+ Do NOT edit this file directly! -- Edit PPPort_pm.PL and the
+ includes in parts/inc/ instead.
+
+ Use 'perldoc ppport.h' to view the documentation below.
+
+----------------------------------------------------------------------
+
+SKIP
+
+=pod
+
+=head1 NAME
+
+ppport.h - Perl/Pollution/Portability version 3.13
+
+=head1 SYNOPSIS
+
+ perl ppport.h [options] [source files]
+
+ Searches current directory for files if no [source files] are given
+
+ --help show short help
+
+ --version show version
+
+ --patch=file write one patch file with changes
+ --copy=suffix write changed copies with suffix
+ --diff=program use diff program and options
+
+ --compat-version=version provide compatibility with Perl version
+ --cplusplus accept C++ comments
+
+ --quiet don't output anything except fatal errors
+ --nodiag don't show diagnostics
+ --nohints don't show hints
+ --nochanges don't suggest changes
+ --nofilter don't filter input files
+
+ --strip strip all script and doc functionality from
+ ppport.h
+
+ --list-provided list provided API
+ --list-unsupported list unsupported API
+ --api-info=name show Perl API portability information
+
+=head1 COMPATIBILITY
+
+This version of F<ppport.h> is designed to support operation with Perl
+installations back to 5.003, and has been tested up to 5.10.0.
+
+=head1 OPTIONS
+
+=head2 --help
+
+Display a brief usage summary.
+
+=head2 --version
+
+Display the version of F<ppport.h>.
+
+=head2 --patch=I<file>
+
+If this option is given, a single patch file will be created if
+any changes are suggested. This requires a working diff program
+to be installed on your system.
+
+=head2 --copy=I<suffix>
+
+If this option is given, a copy of each file will be saved with
+the given suffix that contains the suggested changes. This does
+not require any external programs. Note that this does not
+automagially add a dot between the original filename and the
+suffix. If you want the dot, you have to include it in the option
+argument.
+
+If neither C<--patch> or C<--copy> are given, the default is to
+simply print the diffs for each file. This requires either
+C<Text::Diff> or a C<diff> program to be installed.
+
+=head2 --diff=I<program>
+
+Manually set the diff program and options to use. The default
+is to use C<Text::Diff>, when installed, and output unified
+context diffs.
+
+=head2 --compat-version=I<version>
+
+Tell F<ppport.h> to check for compatibility with the given
+Perl version. The default is to check for compatibility with Perl
+version 5.003. You can use this option to reduce the output
+of F<ppport.h> if you intend to be backward compatible only
+down to a certain Perl version.
+
+=head2 --cplusplus
+
+Usually, F<ppport.h> will detect C++ style comments and
+replace them with C style comments for portability reasons.
+Using this option instructs F<ppport.h> to leave C++
+comments untouched.
+
+=head2 --quiet
+
+Be quiet. Don't print anything except fatal errors.
+
+=head2 --nodiag
+
+Don't output any diagnostic messages. Only portability
+alerts will be printed.
+
+=head2 --nohints
+
+Don't output any hints. Hints often contain useful portability
+notes. Warnings will still be displayed.
+
+=head2 --nochanges
+
+Don't suggest any changes. Only give diagnostic output and hints
+unless these are also deactivated.
+
+=head2 --nofilter
+
+Don't filter the list of input files. By default, files not looking
+like source code (i.e. not *.xs, *.c, *.cc, *.cpp or *.h) are skipped.
+
+=head2 --strip
+
+Strip all script and documentation functionality from F<ppport.h>.
+This reduces the size of F<ppport.h> dramatically and may be useful
+if you want to include F<ppport.h> in smaller modules without
+increasing their distribution size too much.
+
+The stripped F<ppport.h> will have a C<--unstrip> option that allows
+you to undo the stripping, but only if an appropriate C<Devel::PPPort>
+module is installed.
+
+=head2 --list-provided
+
+Lists the API elements for which compatibility is provided by
+F<ppport.h>. Also lists if it must be explicitly requested,
+if it has dependencies, and if there are hints or warnings for it.
+
+=head2 --list-unsupported
+
+Lists the API elements that are known not to be supported by
+F<ppport.h> and below which version of Perl they probably
+won't be available or work.
+
+=head2 --api-info=I<name>
+
+Show portability information for API elements matching I<name>.
+If I<name> is surrounded by slashes, it is interpreted as a regular
+expression.
+
+=head1 DESCRIPTION
+
+In order for a Perl extension (XS) module to be as portable as possible
+across differing versions of Perl itself, certain steps need to be taken.
+
+=over 4
+
+=item *
+
+Including this header is the first major one. This alone will give you
+access to a large part of the Perl API that hasn't been available in
+earlier Perl releases. Use
+
+ perl ppport.h --list-provided
+
+to see which API elements are provided by ppport.h.
+
+=item *
+
+You should avoid using deprecated parts of the API. For example, using
+global Perl variables without the C<PL_> prefix is deprecated. Also,
+some API functions used to have a C<perl_> prefix. Using this form is
+also deprecated. You can safely use the supported API, as F<ppport.h>
+will provide wrappers for older Perl versions.
+
+=item *
+
+If you use one of a few functions or variables that were not present in
+earlier versions of Perl, and that can't be provided using a macro, you
+have to explicitly request support for these functions by adding one or
+more C<#define>s in your source code before the inclusion of F<ppport.h>.
+
+These functions or variables will be marked C<explicit> in the list shown
+by C<--list-provided>.
+
+Depending on whether you module has a single or multiple files that
+use such functions or variables, you want either C<static> or global
+variants.
+
+For a C<static> function or variable (used only in a single source
+file), use:
+
+ #define NEED_function
+ #define NEED_variable
+
+For a global function or variable (used in multiple source files),
+use:
+
+ #define NEED_function_GLOBAL
+ #define NEED_variable_GLOBAL
+
+Note that you mustn't have more than one global request for the
+same function or variable in your project.
+
+ Function / Variable Static Request Global Request
+ -----------------------------------------------------------------------------------------
+ PL_signals NEED_PL_signals NEED_PL_signals_GLOBAL
+ eval_pv() NEED_eval_pv NEED_eval_pv_GLOBAL
+ grok_bin() NEED_grok_bin NEED_grok_bin_GLOBAL
+ grok_hex() NEED_grok_hex NEED_grok_hex_GLOBAL
+ grok_number() NEED_grok_number NEED_grok_number_GLOBAL
+ grok_numeric_radix() NEED_grok_numeric_radix NEED_grok_numeric_radix_GLOBAL
+ grok_oct() NEED_grok_oct NEED_grok_oct_GLOBAL
+ load_module() NEED_load_module NEED_load_module_GLOBAL
+ my_snprintf() NEED_my_snprintf NEED_my_snprintf_GLOBAL
+ my_strlcat() NEED_my_strlcat NEED_my_strlcat_GLOBAL
+ my_strlcpy() NEED_my_strlcpy NEED_my_strlcpy_GLOBAL
+ newCONSTSUB() NEED_newCONSTSUB NEED_newCONSTSUB_GLOBAL
+ newRV_noinc() NEED_newRV_noinc NEED_newRV_noinc_GLOBAL
+ newSVpvn_share() NEED_newSVpvn_share NEED_newSVpvn_share_GLOBAL
+ sv_2pv_flags() NEED_sv_2pv_flags NEED_sv_2pv_flags_GLOBAL
+ sv_2pvbyte() NEED_sv_2pvbyte NEED_sv_2pvbyte_GLOBAL
+ sv_catpvf_mg() NEED_sv_catpvf_mg NEED_sv_catpvf_mg_GLOBAL
+ sv_catpvf_mg_nocontext() NEED_sv_catpvf_mg_nocontext NEED_sv_catpvf_mg_nocontext_GLOBAL
+ sv_pvn_force_flags() NEED_sv_pvn_force_flags NEED_sv_pvn_force_flags_GLOBAL
+ sv_setpvf_mg() NEED_sv_setpvf_mg NEED_sv_setpvf_mg_GLOBAL
+ sv_setpvf_mg_nocontext() NEED_sv_setpvf_mg_nocontext NEED_sv_setpvf_mg_nocontext_GLOBAL
+ vload_module() NEED_vload_module NEED_vload_module_GLOBAL
+ vnewSVpvf() NEED_vnewSVpvf NEED_vnewSVpvf_GLOBAL
+ warner() NEED_warner NEED_warner_GLOBAL
+
+To avoid namespace conflicts, you can change the namespace of the
+explicitly exported functions / variables using the C<DPPP_NAMESPACE>
+macro. Just C<#define> the macro before including C<ppport.h>:
+
+ #define DPPP_NAMESPACE MyOwnNamespace_
+ #include "ppport.h"
+
+The default namespace is C<DPPP_>.
+
+=back
+
+The good thing is that most of the above can be checked by running
+F<ppport.h> on your source code. See the next section for
+details.
+
+=head1 EXAMPLES
+
+To verify whether F<ppport.h> is needed for your module, whether you
+should make any changes to your code, and whether any special defines
+should be used, F<ppport.h> can be run as a Perl script to check your
+source code. Simply say:
+
+ perl ppport.h
+
+The result will usually be a list of patches suggesting changes
+that should at least be acceptable, if not necessarily the most
+efficient solution, or a fix for all possible problems.
+
+If you know that your XS module uses features only available in
+newer Perl releases, if you're aware that it uses C++ comments,
+and if you want all suggestions as a single patch file, you could
+use something like this:
+
+ perl ppport.h --compat-version=5.6.0 --cplusplus --patch=test.diff
+
+If you only want your code to be scanned without any suggestions
+for changes, use:
+
+ perl ppport.h --nochanges
+
+You can specify a different C<diff> program or options, using
+the C<--diff> option:
+
+ perl ppport.h --diff='diff -C 10'
+
+This would output context diffs with 10 lines of context.
+
+If you want to create patched copies of your files instead, use:
+
+ perl ppport.h --copy=.new
+
+To display portability information for the C<newSVpvn> function,
+use:
+
+ perl ppport.h --api-info=newSVpvn
+
+Since the argument to C<--api-info> can be a regular expression,
+you can use
+
+ perl ppport.h --api-info=/_nomg$/
+
+to display portability information for all C<_nomg> functions or
+
+ perl ppport.h --api-info=/./
+
+to display information for all known API elements.
+
+=head1 BUGS
+
+If this version of F<ppport.h> is causing failure during
+the compilation of this module, please check if newer versions
+of either this module or C<Devel::PPPort> are available on CPAN
+before sending a bug report.
+
+If F<ppport.h> was generated using the latest version of
+C<Devel::PPPort> and is causing failure of this module, please
+file a bug report using the CPAN Request Tracker at L<http://rt.cpan.org/>.
+
+Please include the following information:
+
+=over 4
+
+=item 1.
+
+The complete output from running "perl -V"
+
+=item 2.
+
+This file.
+
+=item 3.
+
+The name and version of the module you were trying to build.
+
+=item 4.
+
+A full log of the build that failed.
+
+=item 5.
+
+Any other information that you think could be relevant.
+
+=back
+
+For the latest version of this code, please get the C<Devel::PPPort>
+module from CPAN.
+
+=head1 COPYRIGHT
+
+Version 3.x, Copyright (c) 2004-2007, Marcus Holland-Moritz.
+
+Version 2.x, Copyright (C) 2001, Paul Marquess.
+
+Version 1.x, Copyright (C) 1999, Kenneth Albanowski.
+
+This program is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+=head1 SEE ALSO
+
+See L<Devel::PPPort>.
+
+=cut
+
+use strict;
+
+# Disable broken TRIE-optimization
+BEGIN { eval '${^RE_TRIE_MAXBUF} = -1' if $] >= 5.009004 && $] <= 5.009005 }
+
+my $VERSION = 3.13;
+
+my %opt = (
+ quiet => 0,
+ diag => 1,
+ hints => 1,
+ changes => 1,
+ cplusplus => 0,
+ filter => 1,
+ strip => 0,
+ version => 0,
+);
+
+my($ppport) = $0 =~ /([\w.]+)$/;
+my $LF = '(?:\r\n|[\r\n])'; # line feed
+my $HS = "[ \t]"; # horizontal whitespace
+
+# Never use C comments in this file!
+my $ccs = '/'.'*';
+my $cce = '*'.'/';
+my $rccs = quotemeta $ccs;
+my $rcce = quotemeta $cce;
+
+eval {
+ require Getopt::Long;
+ Getopt::Long::GetOptions(\%opt, qw(
+ help quiet diag! filter! hints! changes! cplusplus strip version
+ patch=s copy=s diff=s compat-version=s
+ list-provided list-unsupported api-info=s
+ )) or usage();
+};
+
+if ($@ and grep /^-/, @ARGV) {
+ usage() if "@ARGV" =~ /^--?h(?:elp)?$/;
+ die "Getopt::Long not found. Please don't use any options.\n";
+}
+
+if ($opt{version}) {
+ print "This is $0 $VERSION.\n";
+ exit 0;
+}
+
+usage() if $opt{help};
+strip() if $opt{strip};
+
+if (exists $opt{'compat-version'}) {
+ my($r,$v,$s) = eval { parse_version($opt{'compat-version'}) };
+ if ($@) {
+ die "Invalid version number format: '$opt{'compat-version'}'\n";
+ }
+ die "Only Perl 5 is supported\n" if $r != 5;
+ die "Invalid version number: $opt{'compat-version'}\n" if $v >= 1000 || $s >= 1000;
+ $opt{'compat-version'} = sprintf "%d.%03d%03d", $r, $v, $s;
+}
+else {
+ $opt{'compat-version'} = 5;
+}
+
+my %API = map { /^(\w+)\|([^|]*)\|([^|]*)\|(\w*)$/
+ ? ( $1 => {
+ ($2 ? ( base => $2 ) : ()),
+ ($3 ? ( todo => $3 ) : ()),
+ (index($4, 'v') >= 0 ? ( varargs => 1 ) : ()),
+ (index($4, 'p') >= 0 ? ( provided => 1 ) : ()),
+ (index($4, 'n') >= 0 ? ( nothxarg => 1 ) : ()),
+ } )
+ : die "invalid spec: $_" } qw(
+AvFILLp|5.004050||p
+AvFILL|||
+CLASS|||n
+CX_CURPAD_SAVE|||
+CX_CURPAD_SV|||
+CopFILEAV|5.006000||p
+CopFILEGV_set|5.006000||p
+CopFILEGV|5.006000||p
+CopFILESV|5.006000||p
+CopFILE_set|5.006000||p
+CopFILE|5.006000||p
+CopSTASHPV_set|5.006000||p
+CopSTASHPV|5.006000||p
+CopSTASH_eq|5.006000||p
+CopSTASH_set|5.006000||p
+CopSTASH|5.006000||p
+CopyD|5.009002||p
+Copy|||
+CvPADLIST|||
+CvSTASH|||
+CvWEAKOUTSIDE|||
+DEFSV|5.004050||p
+END_EXTERN_C|5.005000||p
+ENTER|||
+ERRSV|5.004050||p
+EXTEND|||
+EXTERN_C|5.005000||p
+F0convert|||n
+FREETMPS|||
+GIMME_V||5.004000|n
+GIMME|||n
+GROK_NUMERIC_RADIX|5.007002||p
+G_ARRAY|||
+G_DISCARD|||
+G_EVAL|||
+G_NOARGS|||
+G_SCALAR|||
+G_VOID||5.004000|
+GetVars|||
+GvSV|||
+Gv_AMupdate|||
+HEf_SVKEY||5.004000|
+HeHASH||5.004000|
+HeKEY||5.004000|
+HeKLEN||5.004000|
+HePV||5.004000|
+HeSVKEY_force||5.004000|
+HeSVKEY_set||5.004000|
+HeSVKEY||5.004000|
+HeVAL||5.004000|
+HvNAME|||
+INT2PTR|5.006000||p
+IN_LOCALE_COMPILETIME|5.007002||p
+IN_LOCALE_RUNTIME|5.007002||p
+IN_LOCALE|5.007002||p
+IN_PERL_COMPILETIME|5.008001||p
+IS_NUMBER_GREATER_THAN_UV_MAX|5.007002||p
+IS_NUMBER_INFINITY|5.007002||p
+IS_NUMBER_IN_UV|5.007002||p
+IS_NUMBER_NAN|5.007003||p
+IS_NUMBER_NEG|5.007002||p
+IS_NUMBER_NOT_INT|5.007002||p
+IVSIZE|5.006000||p
+IVTYPE|5.006000||p
+IVdf|5.006000||p
+LEAVE|||
+LVRET|||
+MARK|||
+MULTICALL||5.009005|
+MY_CXT_CLONE|5.009002||p
+MY_CXT_INIT|5.007003||p
+MY_CXT|5.007003||p
+MoveD|5.009002||p
+Move|||
+NOOP|5.005000||p
+NUM2PTR|5.006000||p
+NVTYPE|5.006000||p
+NVef|5.006001||p
+NVff|5.006001||p
+NVgf|5.006001||p
+Newxc|5.009003||p
+Newxz|5.009003||p
+Newx|5.009003||p
+Nullav|||
+Nullch|||
+Nullcv|||
+Nullhv|||
+Nullsv|||
+ORIGMARK|||
+PAD_BASE_SV|||
+PAD_CLONE_VARS|||
+PAD_COMPNAME_FLAGS|||
+PAD_COMPNAME_GEN_set|||
+PAD_COMPNAME_GEN|||
+PAD_COMPNAME_OURSTASH|||
+PAD_COMPNAME_PV|||
+PAD_COMPNAME_TYPE|||
+PAD_RESTORE_LOCAL|||
+PAD_SAVE_LOCAL|||
+PAD_SAVE_SETNULLPAD|||
+PAD_SETSV|||
+PAD_SET_CUR_NOSAVE|||
+PAD_SET_CUR|||
+PAD_SVl|||
+PAD_SV|||
+PERL_ABS|5.008001||p
+PERL_BCDVERSION|5.009005||p
+PERL_GCC_BRACE_GROUPS_FORBIDDEN|5.008001||p
+PERL_HASH|5.004000||p
+PERL_INT_MAX|5.004000||p
+PERL_INT_MIN|5.004000||p
+PERL_LONG_MAX|5.004000||p
+PERL_LONG_MIN|5.004000||p
+PERL_MAGIC_arylen|5.007002||p
+PERL_MAGIC_backref|5.007002||p
+PERL_MAGIC_bm|5.007002||p
+PERL_MAGIC_collxfrm|5.007002||p
+PERL_MAGIC_dbfile|5.007002||p
+PERL_MAGIC_dbline|5.007002||p
+PERL_MAGIC_defelem|5.007002||p
+PERL_MAGIC_envelem|5.007002||p
+PERL_MAGIC_env|5.007002||p
+PERL_MAGIC_ext|5.007002||p
+PERL_MAGIC_fm|5.007002||p
+PERL_MAGIC_glob|5.009005||p
+PERL_MAGIC_isaelem|5.007002||p
+PERL_MAGIC_isa|5.007002||p
+PERL_MAGIC_mutex|5.009005||p
+PERL_MAGIC_nkeys|5.007002||p
+PERL_MAGIC_overload_elem|5.007002||p
+PERL_MAGIC_overload_table|5.007002||p
+PERL_MAGIC_overload|5.007002||p
+PERL_MAGIC_pos|5.007002||p
+PERL_MAGIC_qr|5.007002||p
+PERL_MAGIC_regdata|5.007002||p
+PERL_MAGIC_regdatum|5.007002||p
+PERL_MAGIC_regex_global|5.007002||p
+PERL_MAGIC_shared_scalar|5.007003||p
+PERL_MAGIC_shared|5.007003||p
+PERL_MAGIC_sigelem|5.007002||p
+PERL_MAGIC_sig|5.007002||p
+PERL_MAGIC_substr|5.007002||p
+PERL_MAGIC_sv|5.007002||p
+PERL_MAGIC_taint|5.007002||p
+PERL_MAGIC_tiedelem|5.007002||p
+PERL_MAGIC_tiedscalar|5.007002||p
+PERL_MAGIC_tied|5.007002||p
+PERL_MAGIC_utf8|5.008001||p
+PERL_MAGIC_uvar_elem|5.007003||p
+PERL_MAGIC_uvar|5.007002||p
+PERL_MAGIC_vec|5.007002||p
+PERL_MAGIC_vstring|5.008001||p
+PERL_QUAD_MAX|5.004000||p
+PERL_QUAD_MIN|5.004000||p
+PERL_REVISION|5.006000||p
+PERL_SCAN_ALLOW_UNDERSCORES|5.007003||p
+PERL_SCAN_DISALLOW_PREFIX|5.007003||p
+PERL_SCAN_GREATER_THAN_UV_MAX|5.007003||p
+PERL_SCAN_SILENT_ILLDIGIT|5.008001||p
+PERL_SHORT_MAX|5.004000||p
+PERL_SHORT_MIN|5.004000||p
+PERL_SIGNALS_UNSAFE_FLAG|5.008001||p
+PERL_SUBVERSION|5.006000||p
+PERL_UCHAR_MAX|5.004000||p
+PERL_UCHAR_MIN|5.004000||p
+PERL_UINT_MAX|5.004000||p
+PERL_UINT_MIN|5.004000||p
+PERL_ULONG_MAX|5.004000||p
+PERL_ULONG_MIN|5.004000||p
+PERL_UNUSED_ARG|5.009003||p
+PERL_UNUSED_CONTEXT|5.009004||p
+PERL_UNUSED_DECL|5.007002||p
+PERL_UNUSED_VAR|5.007002||p
+PERL_UQUAD_MAX|5.004000||p
+PERL_UQUAD_MIN|5.004000||p
+PERL_USE_GCC_BRACE_GROUPS|5.009004||p
+PERL_USHORT_MAX|5.004000||p
+PERL_USHORT_MIN|5.004000||p
+PERL_VERSION|5.006000||p
+PL_DBsignal|5.005000||p
+PL_DBsingle|||pn
+PL_DBsub|||pn
+PL_DBtrace|||pn
+PL_Sv|5.005000||p
+PL_compiling|5.004050||p
+PL_copline|5.009005||p
+PL_curcop|5.004050||p
+PL_curstash|5.004050||p
+PL_debstash|5.004050||p
+PL_defgv|5.004050||p
+PL_diehook|5.004050||p
+PL_dirty|5.004050||p
+PL_dowarn|||pn
+PL_errgv|5.004050||p
+PL_expect|5.009005||p
+PL_hexdigit|5.005000||p
+PL_hints|5.005000||p
+PL_last_in_gv|||n
+PL_laststatval|5.005000||p
+PL_modglobal||5.005000|n
+PL_na|5.004050||pn
+PL_no_modify|5.006000||p
+PL_ofs_sv|||n
+PL_perl_destruct_level|5.004050||p
+PL_perldb|5.004050||p
+PL_ppaddr|5.006000||p
+PL_rsfp_filters|5.004050||p
+PL_rsfp|5.004050||p
+PL_rs|||n
+PL_signals|5.008001||p
+PL_stack_base|5.004050||p
+PL_stack_sp|5.004050||p
+PL_statcache|5.005000||p
+PL_stdingv|5.004050||p
+PL_sv_arenaroot|5.004050||p
+PL_sv_no|5.004050||pn
+PL_sv_undef|5.004050||pn
+PL_sv_yes|5.004050||pn
+PL_tainted|5.004050||p
+PL_tainting|5.004050||p
+POP_MULTICALL||5.009005|
+POPi|||n
+POPl|||n
+POPn|||n
+POPpbytex||5.007001|n
+POPpx||5.005030|n
+POPp|||n
+POPs|||n
+PTR2IV|5.006000||p
+PTR2NV|5.006000||p
+PTR2UV|5.006000||p
+PTR2ul|5.007001||p
+PTRV|5.006000||p
+PUSHMARK|||
+PUSH_MULTICALL||5.009005|
+PUSHi|||
+PUSHmortal|5.009002||p
+PUSHn|||
+PUSHp|||
+PUSHs|||
+PUSHu|5.004000||p
+PUTBACK|||
+PerlIO_clearerr||5.007003|
+PerlIO_close||5.007003|
+PerlIO_context_layers||5.009004|
+PerlIO_eof||5.007003|
+PerlIO_error||5.007003|
+PerlIO_fileno||5.007003|
+PerlIO_fill||5.007003|
+PerlIO_flush||5.007003|
+PerlIO_get_base||5.007003|
+PerlIO_get_bufsiz||5.007003|
+PerlIO_get_cnt||5.007003|
+PerlIO_get_ptr||5.007003|
+PerlIO_read||5.007003|
+PerlIO_seek||5.007003|
+PerlIO_set_cnt||5.007003|
+PerlIO_set_ptrcnt||5.007003|
+PerlIO_setlinebuf||5.007003|
+PerlIO_stderr||5.007003|
+PerlIO_stdin||5.007003|
+PerlIO_stdout||5.007003|
+PerlIO_tell||5.007003|
+PerlIO_unread||5.007003|
+PerlIO_write||5.007003|
+Perl_signbit||5.009005|n
+PoisonFree|5.009004||p
+PoisonNew|5.009004||p
+PoisonWith|5.009004||p
+Poison|5.008000||p
+RETVAL|||n
+Renewc|||
+Renew|||
+SAVECLEARSV|||
+SAVECOMPPAD|||
+SAVEPADSV|||
+SAVETMPS|||
+SAVE_DEFSV|5.004050||p
+SPAGAIN|||
+SP|||
+START_EXTERN_C|5.005000||p
+START_MY_CXT|5.007003||p
+STMT_END|||p
+STMT_START|||p
+STR_WITH_LEN|5.009003||p
+ST|||
+SV_CONST_RETURN|5.009003||p
+SV_COW_DROP_PV|5.008001||p
+SV_COW_SHARED_HASH_KEYS|5.009005||p
+SV_GMAGIC|5.007002||p
+SV_HAS_TRAILING_NUL|5.009004||p
+SV_IMMEDIATE_UNREF|5.007001||p
+SV_MUTABLE_RETURN|5.009003||p
+SV_NOSTEAL|5.009002||p
+SV_SMAGIC|5.009003||p
+SV_UTF8_NO_ENCODING|5.008001||p
+SVf|5.006000||p
+SVt_IV|||
+SVt_NV|||
+SVt_PVAV|||
+SVt_PVCV|||
+SVt_PVHV|||
+SVt_PVMG|||
+SVt_PV|||
+Safefree|||
+Slab_Alloc|||
+Slab_Free|||
+Slab_to_rw|||
+StructCopy|||
+SvCUR_set|||
+SvCUR|||
+SvEND|||
+SvGAMAGIC||5.006001|
+SvGETMAGIC|5.004050||p
+SvGROW|||
+SvIOK_UV||5.006000|
+SvIOK_notUV||5.006000|
+SvIOK_off|||
+SvIOK_only_UV||5.006000|
+SvIOK_only|||
+SvIOK_on|||
+SvIOKp|||
+SvIOK|||
+SvIVX|||
+SvIV_nomg|5.009001||p
+SvIV_set|||
+SvIVx|||
+SvIV|||
+SvIsCOW_shared_hash||5.008003|
+SvIsCOW||5.008003|
+SvLEN_set|||
+SvLEN|||
+SvLOCK||5.007003|
+SvMAGIC_set|5.009003||p
+SvNIOK_off|||
+SvNIOKp|||
+SvNIOK|||
+SvNOK_off|||
+SvNOK_only|||
+SvNOK_on|||
+SvNOKp|||
+SvNOK|||
+SvNVX|||
+SvNV_set|||
+SvNVx|||
+SvNV|||
+SvOK|||
+SvOOK|||
+SvPOK_off|||
+SvPOK_only_UTF8||5.006000|
+SvPOK_only|||
+SvPOK_on|||
+SvPOKp|||
+SvPOK|||
+SvPVX_const|5.009003||p
+SvPVX_mutable|5.009003||p
+SvPVX|||
+SvPV_const|5.009003||p
+SvPV_flags_const_nolen|5.009003||p
+SvPV_flags_const|5.009003||p
+SvPV_flags_mutable|5.009003||p
+SvPV_flags|5.007002||p
+SvPV_force_flags_mutable|5.009003||p
+SvPV_force_flags_nolen|5.009003||p
+SvPV_force_flags|5.007002||p
+SvPV_force_mutable|5.009003||p
+SvPV_force_nolen|5.009003||p
+SvPV_force_nomg_nolen|5.009003||p
+SvPV_force_nomg|5.007002||p
+SvPV_force|||p
+SvPV_mutable|5.009003||p
+SvPV_nolen_const|5.009003||p
+SvPV_nolen|5.006000||p
+SvPV_nomg_const_nolen|5.009003||p
+SvPV_nomg_const|5.009003||p
+SvPV_nomg|5.007002||p
+SvPV_set|||
+SvPVbyte_force||5.009002|
+SvPVbyte_nolen||5.006000|
+SvPVbytex_force||5.006000|
+SvPVbytex||5.006000|
+SvPVbyte|5.006000||p
+SvPVutf8_force||5.006000|
+SvPVutf8_nolen||5.006000|
+SvPVutf8x_force||5.006000|
+SvPVutf8x||5.006000|
+SvPVutf8||5.006000|
+SvPVx|||
+SvPV|||
+SvREFCNT_dec|||
+SvREFCNT_inc_NN|5.009004||p
+SvREFCNT_inc_simple_NN|5.009004||p
+SvREFCNT_inc_simple_void_NN|5.009004||p
+SvREFCNT_inc_simple_void|5.009004||p
+SvREFCNT_inc_simple|5.009004||p
+SvREFCNT_inc_void_NN|5.009004||p
+SvREFCNT_inc_void|5.009004||p
+SvREFCNT_inc|||p
+SvREFCNT|||
+SvROK_off|||
+SvROK_on|||
+SvROK|||
+SvRV_set|5.009003||p
+SvRV|||
+SvRXOK||5.009005|
+SvRX||5.009005|
+SvSETMAGIC|||
+SvSHARED_HASH|5.009003||p
+SvSHARE||5.007003|
+SvSTASH_set|5.009003||p
+SvSTASH|||
+SvSetMagicSV_nosteal||5.004000|
+SvSetMagicSV||5.004000|
+SvSetSV_nosteal||5.004000|
+SvSetSV|||
+SvTAINTED_off||5.004000|
+SvTAINTED_on||5.004000|
+SvTAINTED||5.004000|
+SvTAINT|||
+SvTRUE|||
+SvTYPE|||
+SvUNLOCK||5.007003|
+SvUOK|5.007001|5.006000|p
+SvUPGRADE|||
+SvUTF8_off||5.006000|
+SvUTF8_on||5.006000|
+SvUTF8||5.006000|
+SvUVXx|5.004000||p
+SvUVX|5.004000||p
+SvUV_nomg|5.009001||p
+SvUV_set|5.009003||p
+SvUVx|5.004000||p
+SvUV|5.004000||p
+SvVOK||5.008001|
+SvVSTRING_mg|5.009004||p
+THIS|||n
+UNDERBAR|5.009002||p
+UTF8_MAXBYTES|5.009002||p
+UVSIZE|5.006000||p
+UVTYPE|5.006000||p
+UVXf|5.007001||p
+UVof|5.006000||p
+UVuf|5.006000||p
+UVxf|5.006000||p
+WARN_ALL|5.006000||p
+WARN_AMBIGUOUS|5.006000||p
+WARN_ASSERTIONS|5.009005||p
+WARN_BAREWORD|5.006000||p
+WARN_CLOSED|5.006000||p
+WARN_CLOSURE|5.006000||p
+WARN_DEBUGGING|5.006000||p
+WARN_DEPRECATED|5.006000||p
+WARN_DIGIT|5.006000||p
+WARN_EXEC|5.006000||p
+WARN_EXITING|5.006000||p
+WARN_GLOB|5.006000||p
+WARN_INPLACE|5.006000||p
+WARN_INTERNAL|5.006000||p
+WARN_IO|5.006000||p
+WARN_LAYER|5.008000||p
+WARN_MALLOC|5.006000||p
+WARN_MISC|5.006000||p
+WARN_NEWLINE|5.006000||p
+WARN_NUMERIC|5.006000||p
+WARN_ONCE|5.006000||p
+WARN_OVERFLOW|5.006000||p
+WARN_PACK|5.006000||p
+WARN_PARENTHESIS|5.006000||p
+WARN_PIPE|5.006000||p
+WARN_PORTABLE|5.006000||p
+WARN_PRECEDENCE|5.006000||p
+WARN_PRINTF|5.006000||p
+WARN_PROTOTYPE|5.006000||p
+WARN_QW|5.006000||p
+WARN_RECURSION|5.006000||p
+WARN_REDEFINE|5.006000||p
+WARN_REGEXP|5.006000||p
+WARN_RESERVED|5.006000||p
+WARN_SEMICOLON|5.006000||p
+WARN_SEVERE|5.006000||p
+WARN_SIGNAL|5.006000||p
+WARN_SUBSTR|5.006000||p
+WARN_SYNTAX|5.006000||p
+WARN_TAINT|5.006000||p
+WARN_THREADS|5.008000||p
+WARN_UNINITIALIZED|5.006000||p
+WARN_UNOPENED|5.006000||p
+WARN_UNPACK|5.006000||p
+WARN_UNTIE|5.006000||p
+WARN_UTF8|5.006000||p
+WARN_VOID|5.006000||p
+XCPT_CATCH|5.009002||p
+XCPT_RETHROW|5.009002||p
+XCPT_TRY_END|5.009002||p
+XCPT_TRY_START|5.009002||p
+XPUSHi|||
+XPUSHmortal|5.009002||p
+XPUSHn|||
+XPUSHp|||
+XPUSHs|||
+XPUSHu|5.004000||p
+XSRETURN_EMPTY|||
+XSRETURN_IV|||
+XSRETURN_NO|||
+XSRETURN_NV|||
+XSRETURN_PV|||
+XSRETURN_UNDEF|||
+XSRETURN_UV|5.008001||p
+XSRETURN_YES|||
+XSRETURN|||p
+XST_mIV|||
+XST_mNO|||
+XST_mNV|||
+XST_mPV|||
+XST_mUNDEF|||
+XST_mUV|5.008001||p
+XST_mYES|||
+XS_VERSION_BOOTCHECK|||
+XS_VERSION|||
+XSprePUSH|5.006000||p
+XS|||
+ZeroD|5.009002||p
+Zero|||
+_aMY_CXT|5.007003||p
+_pMY_CXT|5.007003||p
+aMY_CXT_|5.007003||p
+aMY_CXT|5.007003||p
+aTHXR_|5.009005||p
+aTHXR|5.009005||p
+aTHX_|5.006000||p
+aTHX|5.006000||p
+add_data|||n
+addmad|||
+allocmy|||
+amagic_call|||
+amagic_cmp_locale|||
+amagic_cmp|||
+amagic_i_ncmp|||
+amagic_ncmp|||
+any_dup|||
+ao|||
+append_elem|||
+append_list|||
+append_madprops|||
+apply_attrs_my|||
+apply_attrs_string||5.006001|
+apply_attrs|||
+apply|||
+atfork_lock||5.007003|n
+atfork_unlock||5.007003|n
+av_arylen_p||5.009003|
+av_clear|||
+av_create_and_push||5.009005|
+av_create_and_unshift_one||5.009005|
+av_delete||5.006000|
+av_exists||5.006000|
+av_extend|||
+av_fake|||
+av_fetch|||
+av_fill|||
+av_len|||
+av_make|||
+av_pop|||
+av_push|||
+av_reify|||
+av_shift|||
+av_store|||
+av_undef|||
+av_unshift|||
+ax|||n
+bad_type|||
+bind_match|||
+block_end|||
+block_gimme||5.004000|
+block_start|||
+boolSV|5.004000||p
+boot_core_PerlIO|||
+boot_core_UNIVERSAL|||
+boot_core_mro|||
+boot_core_xsutils|||
+bytes_from_utf8||5.007001|
+bytes_to_uni|||n
+bytes_to_utf8||5.006001|
+call_argv|5.006000||p
+call_atexit||5.006000|
+call_list||5.004000|
+call_method|5.006000||p
+call_pv|5.006000||p
+call_sv|5.006000||p
+calloc||5.007002|n
+cando|||
+cast_i32||5.006000|
+cast_iv||5.006000|
+cast_ulong||5.006000|
+cast_uv||5.006000|
+check_type_and_open|||
+check_uni|||
+checkcomma|||
+checkposixcc|||
+ckWARN|5.006000||p
+ck_anoncode|||
+ck_bitop|||
+ck_concat|||
+ck_defined|||
+ck_delete|||
+ck_die|||
+ck_eof|||
+ck_eval|||
+ck_exec|||
+ck_exists|||
+ck_exit|||
+ck_ftst|||
+ck_fun|||
+ck_glob|||
+ck_grep|||
+ck_index|||
+ck_join|||
+ck_lengthconst|||
+ck_lfun|||
+ck_listiob|||
+ck_match|||
+ck_method|||
+ck_null|||
+ck_open|||
+ck_readline|||
+ck_repeat|||
+ck_require|||
+ck_retarget|||
+ck_return|||
+ck_rfun|||
+ck_rvconst|||
+ck_sassign|||
+ck_select|||
+ck_shift|||
+ck_sort|||
+ck_spair|||
+ck_split|||
+ck_subr|||
+ck_substr|||
+ck_svconst|||
+ck_trunc|||
+ck_unpack|||
+ckwarn_d||5.009003|
+ckwarn||5.009003|
+cl_and|||n
+cl_anything|||n
+cl_init_zero|||n
+cl_init|||n
+cl_is_anything|||n
+cl_or|||n
+clear_placeholders|||
+closest_cop|||
+convert|||
+cop_free|||
+cr_textfilter|||
+create_eval_scope|||
+croak_nocontext|||vn
+croak|||v
+csighandler||5.009003|n
+curmad|||
+custom_op_desc||5.007003|
+custom_op_name||5.007003|
+cv_ckproto_len|||
+cv_ckproto|||
+cv_clone|||
+cv_const_sv||5.004000|
+cv_dump|||
+cv_undef|||
+cx_dump||5.005000|
+cx_dup|||
+cxinc|||
+dAXMARK|5.009003||p
+dAX|5.007002||p
+dITEMS|5.007002||p
+dMARK|||
+dMULTICALL||5.009003|
+dMY_CXT_SV|5.007003||p
+dMY_CXT|5.007003||p
+dNOOP|5.006000||p
+dORIGMARK|||
+dSP|||
+dTHR|5.004050||p
+dTHXR|5.009005||p
+dTHXa|5.006000||p
+dTHXoa|5.006000||p
+dTHX|5.006000||p
+dUNDERBAR|5.009002||p
+dVAR|5.009003||p
+dXCPT|5.009002||p
+dXSARGS|||
+dXSI32|||
+dXSTARG|5.006000||p
+deb_curcv|||
+deb_nocontext|||vn
+deb_stack_all|||
+deb_stack_n|||
+debop||5.005000|
+debprofdump||5.005000|
+debprof|||
+debstackptrs||5.007003|
+debstack||5.007003|
+debug_start_match|||
+deb||5.007003|v
+del_sv|||
+delete_eval_scope|||
+delimcpy||5.004000|
+deprecate_old|||
+deprecate|||
+despatch_signals||5.007001|
+destroy_matcher|||
+die_nocontext|||vn
+die_where|||
+die|||v
+dirp_dup|||
+div128|||
+djSP|||
+do_aexec5|||
+do_aexec|||
+do_aspawn|||
+do_binmode||5.004050|
+do_chomp|||
+do_chop|||
+do_close|||
+do_dump_pad|||
+do_eof|||
+do_exec3|||
+do_execfree|||
+do_exec|||
+do_gv_dump||5.006000|
+do_gvgv_dump||5.006000|
+do_hv_dump||5.006000|
+do_ipcctl|||
+do_ipcget|||
+do_join|||
+do_kv|||
+do_magic_dump||5.006000|
+do_msgrcv|||
+do_msgsnd|||
+do_oddball|||
+do_op_dump||5.006000|
+do_op_xmldump|||
+do_open9||5.006000|
+do_openn||5.007001|
+do_open||5.004000|
+do_pipe|||
+do_pmop_dump||5.006000|
+do_pmop_xmldump|||
+do_print|||
+do_readline|||
+do_seek|||
+do_semop|||
+do_shmio|||
+do_smartmatch|||
+do_spawn_nowait|||
+do_spawn|||
+do_sprintf|||
+do_sv_dump||5.006000|
+do_sysseek|||
+do_tell|||
+do_trans_complex_utf8|||
+do_trans_complex|||
+do_trans_count_utf8|||
+do_trans_count|||
+do_trans_simple_utf8|||
+do_trans_simple|||
+do_trans|||
+do_vecget|||
+do_vecset|||
+do_vop|||
+docatch_body|||
+docatch|||
+doeval|||
+dofile|||
+dofindlabel|||
+doform|||
+doing_taint||5.008001|n
+dooneliner|||
+doopen_pm|||
+doparseform|||
+dopoptoeval|||
+dopoptogiven|||
+dopoptolabel|||
+dopoptoloop|||
+dopoptosub_at|||
+dopoptosub|||
+dopoptowhen|||
+doref||5.009003|
+dounwind|||
+dowantarray|||
+dump_all||5.006000|
+dump_eval||5.006000|
+dump_exec_pos|||
+dump_fds|||
+dump_form||5.006000|
+dump_indent||5.006000|v
+dump_mstats|||
+dump_packsubs||5.006000|
+dump_sub||5.006000|
+dump_sv_child|||
+dump_trie_interim_list|||
+dump_trie_interim_table|||
+dump_trie|||
+dump_vindent||5.006000|
+dumpuntil|||
+dup_attrlist|||
+emulate_cop_io|||
+emulate_eaccess|||
+eval_pv|5.006000||p
+eval_sv|5.006000||p
+exec_failed|||
+expect_number|||
+fbm_compile||5.005000|
+fbm_instr||5.005000|
+fd_on_nosuid_fs|||
+feature_is_enabled|||
+filter_add|||
+filter_del|||
+filter_gets|||
+filter_read|||
+find_and_forget_pmops|||
+find_array_subscript|||
+find_beginning|||
+find_byclass|||
+find_hash_subscript|||
+find_in_my_stash|||
+find_runcv||5.008001|
+find_rundefsvoffset||5.009002|
+find_script|||
+find_uninit_var|||
+first_symbol|||n
+fold_constants|||
+forbid_setid|||
+force_ident|||
+force_list|||
+force_next|||
+force_version|||
+force_word|||
+forget_pmop|||
+form_nocontext|||vn
+form||5.004000|v
+fp_dup|||
+fprintf_nocontext|||vn
+free_global_struct|||
+free_tied_hv_pool|||
+free_tmps|||
+gen_constant_list|||
+get_arena|||
+get_av|5.006000||p
+get_context||5.006000|n
+get_cvn_flags||5.009005|
+get_cv|5.006000||p
+get_db_sub|||
+get_debug_opts|||
+get_hash_seed|||
+get_hv|5.006000||p
+get_mstats|||
+get_no_modify|||
+get_num|||
+get_op_descs||5.005000|
+get_op_names||5.005000|
+get_opargs|||
+get_ppaddr||5.006000|
+get_re_arg|||
+get_sv|5.006000||p
+get_vtbl||5.005030|
+getcwd_sv||5.007002|
+getenv_len|||
+glob_2number|||
+glob_2pv|||
+glob_assign_glob|||
+glob_assign_ref|||
+gp_dup|||
+gp_free|||
+gp_ref|||
+grok_bin|5.007003||p
+grok_hex|5.007003||p
+grok_number|5.007002||p
+grok_numeric_radix|5.007002||p
+grok_oct|5.007003||p
+group_end|||
+gv_AVadd|||
+gv_HVadd|||
+gv_IOadd|||
+gv_SVadd|||
+gv_autoload4||5.004000|
+gv_check|||
+gv_const_sv||5.009003|
+gv_dump||5.006000|
+gv_efullname3||5.004000|
+gv_efullname4||5.006001|
+gv_efullname|||
+gv_ename|||
+gv_fetchfile_flags||5.009005|
+gv_fetchfile|||
+gv_fetchmeth_autoload||5.007003|
+gv_fetchmethod_autoload||5.004000|
+gv_fetchmethod|||
+gv_fetchmeth|||
+gv_fetchpvn_flags||5.009002|
+gv_fetchpv|||
+gv_fetchsv||5.009002|
+gv_fullname3||5.004000|
+gv_fullname4||5.006001|
+gv_fullname|||
+gv_handler||5.007001|
+gv_init_sv|||
+gv_init|||
+gv_name_set||5.009004|
+gv_stashpvn|5.004000||p
+gv_stashpvs||5.009003|
+gv_stashpv|||
+gv_stashsv|||
+he_dup|||
+hek_dup|||
+hfreeentries|||
+hsplit|||
+hv_assert||5.009005|
+hv_auxinit|||n
+hv_backreferences_p|||
+hv_clear_placeholders||5.009001|
+hv_clear|||
+hv_copy_hints_hv|||
+hv_delayfree_ent||5.004000|
+hv_delete_common|||
+hv_delete_ent||5.004000|
+hv_delete|||
+hv_eiter_p||5.009003|
+hv_eiter_set||5.009003|
+hv_exists_ent||5.004000|
+hv_exists|||
+hv_fetch_common|||
+hv_fetch_ent||5.004000|
+hv_fetchs|5.009003||p
+hv_fetch|||
+hv_free_ent||5.004000|
+hv_iterinit|||
+hv_iterkeysv||5.004000|
+hv_iterkey|||
+hv_iternext_flags||5.008000|
+hv_iternextsv|||
+hv_iternext|||
+hv_iterval|||
+hv_kill_backrefs|||
+hv_ksplit||5.004000|
+hv_magic_check|||n
+hv_magic_uvar_xkey|||
+hv_magic|||
+hv_name_set||5.009003|
+hv_notallowed|||
+hv_placeholders_get||5.009003|
+hv_placeholders_p||5.009003|
+hv_placeholders_set||5.009003|
+hv_riter_p||5.009003|
+hv_riter_set||5.009003|
+hv_scalar||5.009001|
+hv_store_ent||5.004000|
+hv_store_flags||5.008000|
+hv_stores|5.009004||p
+hv_store|||
+hv_undef|||
+ibcmp_locale||5.004000|
+ibcmp_utf8||5.007003|
+ibcmp|||
+incl_perldb|||
+incline|||
+incpush_if_exists|||
+incpush|||
+ingroup|||
+init_argv_symbols|||
+init_debugger|||
+init_global_struct|||
+init_i18nl10n||5.006000|
+init_i18nl14n||5.006000|
+init_ids|||
+init_interp|||
+init_main_stash|||
+init_perllib|||
+init_postdump_symbols|||
+init_predump_symbols|||
+init_stacks||5.005000|
+init_tm||5.007002|
+instr|||
+intro_my|||
+intuit_method|||
+intuit_more|||
+invert|||
+io_close|||
+isALNUM|||
+isALPHA|||
+isDIGIT|||
+isLOWER|||
+isSPACE|||
+isUPPER|||
+is_an_int|||
+is_gv_magical_sv|||
+is_gv_magical|||
+is_handle_constructor|||n
+is_list_assignment|||
+is_lvalue_sub||5.007001|
+is_uni_alnum_lc||5.006000|
+is_uni_alnumc_lc||5.006000|
+is_uni_alnumc||5.006000|
+is_uni_alnum||5.006000|
+is_uni_alpha_lc||5.006000|
+is_uni_alpha||5.006000|
+is_uni_ascii_lc||5.006000|
+is_uni_ascii||5.006000|
+is_uni_cntrl_lc||5.006000|
+is_uni_cntrl||5.006000|
+is_uni_digit_lc||5.006000|
+is_uni_digit||5.006000|
+is_uni_graph_lc||5.006000|
+is_uni_graph||5.006000|
+is_uni_idfirst_lc||5.006000|
+is_uni_idfirst||5.006000|
+is_uni_lower_lc||5.006000|
+is_uni_lower||5.006000|
+is_uni_print_lc||5.006000|
+is_uni_print||5.006000|
+is_uni_punct_lc||5.006000|
+is_uni_punct||5.006000|
+is_uni_space_lc||5.006000|
+is_uni_space||5.006000|
+is_uni_upper_lc||5.006000|
+is_uni_upper||5.006000|
+is_uni_xdigit_lc||5.006000|
+is_uni_xdigit||5.006000|
+is_utf8_alnumc||5.006000|
+is_utf8_alnum||5.006000|
+is_utf8_alpha||5.006000|
+is_utf8_ascii||5.006000|
+is_utf8_char_slow|||n
+is_utf8_char||5.006000|
+is_utf8_cntrl||5.006000|
+is_utf8_common|||
+is_utf8_digit||5.006000|
+is_utf8_graph||5.006000|
+is_utf8_idcont||5.008000|
+is_utf8_idfirst||5.006000|
+is_utf8_lower||5.006000|
+is_utf8_mark||5.006000|
+is_utf8_print||5.006000|
+is_utf8_punct||5.006000|
+is_utf8_space||5.006000|
+is_utf8_string_loclen||5.009003|
+is_utf8_string_loc||5.008001|
+is_utf8_string||5.006001|
+is_utf8_upper||5.006000|
+is_utf8_xdigit||5.006000|
+isa_lookup|||
+items|||n
+ix|||n
+jmaybe|||
+join_exact|||
+keyword|||
+leave_scope|||
+lex_end|||
+lex_start|||
+linklist|||
+listkids|||
+list|||
+load_module_nocontext|||vn
+load_module|5.006000||pv
+localize|||
+looks_like_bool|||
+looks_like_number|||
+lop|||
+mPUSHi|5.009002||p
+mPUSHn|5.009002||p
+mPUSHp|5.009002||p
+mPUSHu|5.009002||p
+mXPUSHi|5.009002||p
+mXPUSHn|5.009002||p
+mXPUSHp|5.009002||p
+mXPUSHu|5.009002||p
+mad_free|||
+madlex|||
+madparse|||
+magic_clear_all_env|||
+magic_clearenv|||
+magic_clearhint|||
+magic_clearpack|||
+magic_clearsig|||
+magic_dump||5.006000|
+magic_existspack|||
+magic_freearylen_p|||
+magic_freeovrld|||
+magic_freeregexp|||
+magic_getarylen|||
+magic_getdefelem|||
+magic_getnkeys|||
+magic_getpack|||
+magic_getpos|||
+magic_getsig|||
+magic_getsubstr|||
+magic_gettaint|||
+magic_getuvar|||
+magic_getvec|||
+magic_get|||
+magic_killbackrefs|||
+magic_len|||
+magic_methcall|||
+magic_methpack|||
+magic_nextpack|||
+magic_regdata_cnt|||
+magic_regdatum_get|||
+magic_regdatum_set|||
+magic_scalarpack|||
+magic_set_all_env|||
+magic_setamagic|||
+magic_setarylen|||
+magic_setbm|||
+magic_setcollxfrm|||
+magic_setdbline|||
+magic_setdefelem|||
+magic_setenv|||
+magic_setfm|||
+magic_setglob|||
+magic_sethint|||
+magic_setisa|||
+magic_setmglob|||
+magic_setnkeys|||
+magic_setpack|||
+magic_setpos|||
+magic_setregexp|||
+magic_setsig|||
+magic_setsubstr|||
+magic_settaint|||
+magic_setutf8|||
+magic_setuvar|||
+magic_setvec|||
+magic_set|||
+magic_sizepack|||
+magic_wipepack|||
+magicname|||
+make_matcher|||
+make_trie_failtable|||
+make_trie|||
+malloced_size|||n
+malloc||5.007002|n
+markstack_grow|||
+matcher_matches_sv|||
+measure_struct|||
+memEQ|5.004000||p
+memNE|5.004000||p
+mem_collxfrm|||
+mess_alloc|||
+mess_nocontext|||vn
+mess||5.006000|v
+method_common|||
+mfree||5.007002|n
+mg_clear|||
+mg_copy|||
+mg_dup|||
+mg_find|||
+mg_free|||
+mg_get|||
+mg_length||5.005000|
+mg_localize|||
+mg_magical|||
+mg_set|||
+mg_size||5.005000|
+mini_mktime||5.007002|
+missingterm|||
+mode_from_discipline|||
+modkids|||
+mod|||
+more_bodies|||
+more_sv|||
+moreswitches|||
+mro_get_linear_isa_c3||5.009005|
+mro_get_linear_isa_dfs||5.009005|
+mro_get_linear_isa||5.009005|
+mro_isa_changed_in|||
+mro_meta_dup|||
+mro_meta_init|||
+mro_method_changed_in||5.009005|
+mul128|||
+mulexp10|||n
+my_atof2||5.007002|
+my_atof||5.006000|
+my_attrs|||
+my_bcopy|||n
+my_betoh16|||n
+my_betoh32|||n
+my_betoh64|||n
+my_betohi|||n
+my_betohl|||n
+my_betohs|||n
+my_bzero|||n
+my_chsize|||
+my_clearenv|||
+my_cxt_index|||
+my_cxt_init|||
+my_dirfd||5.009005|
+my_exit_jump|||
+my_exit|||
+my_failure_exit||5.004000|
+my_fflush_all||5.006000|
+my_fork||5.007003|n
+my_htobe16|||n
+my_htobe32|||n
+my_htobe64|||n
+my_htobei|||n
+my_htobel|||n
+my_htobes|||n
+my_htole16|||n
+my_htole32|||n
+my_htole64|||n
+my_htolei|||n
+my_htolel|||n
+my_htoles|||n
+my_htonl|||
+my_kid|||
+my_letoh16|||n
+my_letoh32|||n
+my_letoh64|||n
+my_letohi|||n
+my_letohl|||n
+my_letohs|||n
+my_lstat|||
+my_memcmp||5.004000|n
+my_memset|||n
+my_ntohl|||
+my_pclose||5.004000|
+my_popen_list||5.007001|
+my_popen||5.004000|
+my_setenv|||
+my_snprintf|5.009004||pvn
+my_socketpair||5.007003|n
+my_sprintf||5.009003|vn
+my_stat|||
+my_strftime||5.007002|
+my_strlcat|5.009004||pn
+my_strlcpy|5.009004||pn
+my_swabn|||n
+my_swap|||
+my_unexec|||
+my_vsnprintf||5.009004|n
+my|||
+need_utf8|||n
+newANONATTRSUB||5.006000|
+newANONHASH|||
+newANONLIST|||
+newANONSUB|||
+newASSIGNOP|||
+newATTRSUB||5.006000|
+newAVREF|||
+newAV|||
+newBINOP|||
+newCONDOP|||
+newCONSTSUB|5.004050||p
+newCVREF|||
+newDEFSVOP|||
+newFORM|||
+newFOROP|||
+newGIVENOP||5.009003|
+newGIVWHENOP|||
+newGP|||
+newGVOP|||
+newGVREF|||
+newGVgen|||
+newHVREF|||
+newHVhv||5.005000|
+newHV|||
+newIO|||
+newLISTOP|||
+newLOGOP|||
+newLOOPEX|||
+newLOOPOP|||
+newMADPROP|||
+newMADsv|||
+newMYSUB|||
+newNULLLIST|||
+newOP|||
+newPADOP|||
+newPMOP|||
+newPROG|||
+newPVOP|||
+newRANGE|||
+newRV_inc|5.004000||p
+newRV_noinc|5.004000||p
+newRV|||
+newSLICEOP|||
+newSTATEOP|||
+newSUB|||
+newSVOP|||
+newSVREF|||
+newSV_type||5.009005|
+newSVhek||5.009003|
+newSViv|||
+newSVnv|||
+newSVpvf_nocontext|||vn
+newSVpvf||5.004000|v
+newSVpvn_share|5.007001||p
+newSVpvn|5.004050||p
+newSVpvs_share||5.009003|
+newSVpvs|5.009003||p
+newSVpv|||
+newSVrv|||
+newSVsv|||
+newSVuv|5.006000||p
+newSV|||
+newTOKEN|||
+newUNOP|||
+newWHENOP||5.009003|
+newWHILEOP||5.009003|
+newXS_flags||5.009004|
+newXSproto||5.006000|
+newXS||5.006000|
+new_collate||5.006000|
+new_constant|||
+new_ctype||5.006000|
+new_he|||
+new_logop|||
+new_numeric||5.006000|
+new_stackinfo||5.005000|
+new_version||5.009000|
+new_warnings_bitfield|||
+next_symbol|||
+nextargv|||
+nextchar|||
+ninstr|||
+no_bareword_allowed|||
+no_fh_allowed|||
+no_op|||
+not_a_number|||
+nothreadhook||5.008000|
+nuke_stacks|||
+num_overflow|||n
+offer_nice_chunk|||
+oopsAV|||
+oopsCV|||
+oopsHV|||
+op_clear|||
+op_const_sv|||
+op_dump||5.006000|
+op_free|||
+op_getmad_weak|||
+op_getmad|||
+op_null||5.007002|
+op_refcnt_dec|||
+op_refcnt_inc|||
+op_refcnt_lock||5.009002|
+op_refcnt_unlock||5.009002|
+op_xmldump|||
+open_script|||
+pMY_CXT_|5.007003||p
+pMY_CXT|5.007003||p
+pTHX_|5.006000||p
+pTHX|5.006000||p
+packWARN|5.007003||p
+pack_cat||5.007003|
+pack_rec|||
+package|||
+packlist||5.008001|
+pad_add_anon|||
+pad_add_name|||
+pad_alloc|||
+pad_block_start|||
+pad_check_dup|||
+pad_compname_type|||
+pad_findlex|||
+pad_findmy|||
+pad_fixup_inner_anons|||
+pad_free|||
+pad_leavemy|||
+pad_new|||
+pad_peg|||n
+pad_push|||
+pad_reset|||
+pad_setsv|||
+pad_sv||5.009005|
+pad_swipe|||
+pad_tidy|||
+pad_undef|||
+parse_body|||
+parse_unicode_opts|||
+parser_dup|||
+parser_free|||
+path_is_absolute|||n
+peep|||
+pending_Slabs_to_ro|||
+perl_alloc_using|||n
+perl_alloc|||n
+perl_clone_using|||n
+perl_clone|||n
+perl_construct|||n
+perl_destruct||5.007003|n
+perl_free|||n
+perl_parse||5.006000|n
+perl_run|||n
+pidgone|||
+pm_description|||
+pmflag|||
+pmop_dump||5.006000|
+pmop_xmldump|||
+pmruntime|||
+pmtrans|||
+pop_scope|||
+pregcomp||5.009005|
+pregexec|||
+pregfree|||
+prepend_elem|||
+prepend_madprops|||
+printbuf|||
+printf_nocontext|||vn
+process_special_blocks|||
+ptr_table_clear||5.009005|
+ptr_table_fetch||5.009005|
+ptr_table_find|||n
+ptr_table_free||5.009005|
+ptr_table_new||5.009005|
+ptr_table_split||5.009005|
+ptr_table_store||5.009005|
+push_scope|||
+put_byte|||
+pv_display||5.006000|
+pv_escape||5.009004|
+pv_pretty||5.009004|
+pv_uni_display||5.007003|
+qerror|||
+qsortsvu|||
+re_compile||5.009005|
+re_croak2|||
+re_dup|||
+re_intuit_start||5.009005|
+re_intuit_string||5.006000|
+readpipe_override|||
+realloc||5.007002|n
+reentrant_free|||
+reentrant_init|||
+reentrant_retry|||vn
+reentrant_size|||
+ref_array_or_hash|||
+refcounted_he_chain_2hv|||
+refcounted_he_fetch|||
+refcounted_he_free|||
+refcounted_he_new|||
+refcounted_he_value|||
+refkids|||
+refto|||
+ref||5.009003|
+reg_check_named_buff_matched|||
+reg_named_buff_all||5.009005|
+reg_named_buff_exists||5.009005|
+reg_named_buff_fetch||5.009005|
+reg_named_buff_firstkey||5.009005|
+reg_named_buff_iter|||
+reg_named_buff_nextkey||5.009005|
+reg_named_buff_scalar||5.009005|
+reg_named_buff|||
+reg_namedseq|||
+reg_node|||
+reg_numbered_buff_fetch|||
+reg_numbered_buff_length|||
+reg_numbered_buff_store|||
+reg_qr_package|||
+reg_recode|||
+reg_scan_name|||
+reg_skipcomment|||
+reg_stringify||5.009005|
+reg_temp_copy|||
+reganode|||
+regatom|||
+regbranch|||
+regclass_swash||5.009004|
+regclass|||
+regcppop|||
+regcppush|||
+regcurly|||n
+regdump_extflags|||
+regdump||5.005000|
+regdupe_internal|||
+regexec_flags||5.005000|
+regfree_internal||5.009005|
+reghop3|||n
+reghop4|||n
+reghopmaybe3|||n
+reginclass|||
+reginitcolors||5.006000|
+reginsert|||
+regmatch|||
+regnext||5.005000|
+regpiece|||
+regpposixcc|||
+regprop|||
+regrepeat|||
+regtail_study|||
+regtail|||
+regtry|||
+reguni|||
+regwhite|||n
+reg|||
+repeatcpy|||
+report_evil_fh|||
+report_uninit|||
+require_pv||5.006000|
+require_tie_mod|||
+restore_magic|||
+rninstr|||
+rsignal_restore|||
+rsignal_save|||
+rsignal_state||5.004000|
+rsignal||5.004000|
+run_body|||
+run_user_filter|||
+runops_debug||5.005000|
+runops_standard||5.005000|
+rvpv_dup|||
+rxres_free|||
+rxres_restore|||
+rxres_save|||
+safesyscalloc||5.006000|n
+safesysfree||5.006000|n
+safesysmalloc||5.006000|n
+safesysrealloc||5.006000|n
+same_dirent|||
+save_I16||5.004000|
+save_I32|||
+save_I8||5.006000|
+save_aelem||5.004050|
+save_alloc||5.006000|
+save_aptr|||
+save_ary|||
+save_bool||5.008001|
+save_clearsv|||
+save_delete|||
+save_destructor_x||5.006000|
+save_destructor||5.006000|
+save_freeop|||
+save_freepv|||
+save_freesv|||
+save_generic_pvref||5.006001|
+save_generic_svref||5.005030|
+save_gp||5.004000|
+save_hash|||
+save_hek_flags|||n
+save_helem||5.004050|
+save_hints||5.005000|
+save_hptr|||
+save_int|||
+save_item|||
+save_iv||5.005000|
+save_lines|||
+save_list|||
+save_long|||
+save_magic|||
+save_mortalizesv||5.007001|
+save_nogv|||
+save_op|||
+save_padsv||5.007001|
+save_pptr|||
+save_re_context||5.006000|
+save_scalar_at|||
+save_scalar|||
+save_set_svflags||5.009000|
+save_shared_pvref||5.007003|
+save_sptr|||
+save_svref|||
+save_vptr||5.006000|
+savepvn|||
+savepvs||5.009003|
+savepv|||
+savesharedpvn||5.009005|
+savesharedpv||5.007003|
+savestack_grow_cnt||5.008001|
+savestack_grow|||
+savesvpv||5.009002|
+sawparens|||
+scalar_mod_type|||n
+scalarboolean|||
+scalarkids|||
+scalarseq|||
+scalarvoid|||
+scalar|||
+scan_bin||5.006000|
+scan_commit|||
+scan_const|||
+scan_formline|||
+scan_heredoc|||
+scan_hex|||
+scan_ident|||
+scan_inputsymbol|||
+scan_num||5.007001|
+scan_oct|||
+scan_pat|||
+scan_str|||
+scan_subst|||
+scan_trans|||
+scan_version||5.009001|
+scan_vstring||5.009005|
+scan_word|||
+scope|||
+screaminstr||5.005000|
+seed||5.008001|
+sequence_num|||
+sequence_tail|||
+sequence|||
+set_context||5.006000|n
+set_csh|||
+set_numeric_local||5.006000|
+set_numeric_radix||5.006000|
+set_numeric_standard||5.006000|
+setdefout|||
+setenv_getix|||
+share_hek_flags|||
+share_hek||5.004000|
+si_dup|||
+sighandler|||n
+simplify_sort|||
+skipspace0|||
+skipspace1|||
+skipspace2|||
+skipspace|||
+softref2xv|||
+sortcv_stacked|||
+sortcv_xsub|||
+sortcv|||
+sortsv_flags||5.009003|
+sortsv||5.007003|
+space_join_names_mortal|||
+ss_dup|||
+stack_grow|||
+start_force|||
+start_glob|||
+start_subparse||5.004000|
+stashpv_hvname_match||5.009005|
+stdize_locale|||
+strEQ|||
+strGE|||
+strGT|||
+strLE|||
+strLT|||
+strNE|||
+str_to_version||5.006000|
+strip_return|||
+strnEQ|||
+strnNE|||
+study_chunk|||
+sub_crush_depth|||
+sublex_done|||
+sublex_push|||
+sublex_start|||
+sv_2bool|||
+sv_2cv|||
+sv_2io|||
+sv_2iuv_common|||
+sv_2iuv_non_preserve|||
+sv_2iv_flags||5.009001|
+sv_2iv|||
+sv_2mortal|||
+sv_2nv|||
+sv_2pv_flags|5.007002||p
+sv_2pv_nolen|5.006000||p
+sv_2pvbyte_nolen|5.006000||p
+sv_2pvbyte|5.006000||p
+sv_2pvutf8_nolen||5.006000|
+sv_2pvutf8||5.006000|
+sv_2pv|||
+sv_2uv_flags||5.009001|
+sv_2uv|5.004000||p
+sv_add_arena|||
+sv_add_backref|||
+sv_backoff|||
+sv_bless|||
+sv_cat_decode||5.008001|
+sv_catpv_mg|5.004050||p
+sv_catpvf_mg_nocontext|||pvn
+sv_catpvf_mg|5.006000|5.004000|pv
+sv_catpvf_nocontext|||vn
+sv_catpvf||5.004000|v
+sv_catpvn_flags||5.007002|
+sv_catpvn_mg|5.004050||p
+sv_catpvn_nomg|5.007002||p
+sv_catpvn|||
+sv_catpvs|5.009003||p
+sv_catpv|||
+sv_catsv_flags||5.007002|
+sv_catsv_mg|5.004050||p
+sv_catsv_nomg|5.007002||p
+sv_catsv|||
+sv_catxmlpvn|||
+sv_catxmlsv|||
+sv_chop|||
+sv_clean_all|||
+sv_clean_objs|||
+sv_clear|||
+sv_cmp_locale||5.004000|
+sv_cmp|||
+sv_collxfrm|||
+sv_compile_2op||5.008001|
+sv_copypv||5.007003|
+sv_dec|||
+sv_del_backref|||
+sv_derived_from||5.004000|
+sv_does||5.009004|
+sv_dump|||
+sv_dup|||
+sv_eq|||
+sv_exp_grow|||
+sv_force_normal_flags||5.007001|
+sv_force_normal||5.006000|
+sv_free2|||
+sv_free_arenas|||
+sv_free|||
+sv_gets||5.004000|
+sv_grow|||
+sv_i_ncmp|||
+sv_inc|||
+sv_insert|||
+sv_isa|||
+sv_isobject|||
+sv_iv||5.005000|
+sv_kill_backrefs|||
+sv_len_utf8||5.006000|
+sv_len|||
+sv_magic_portable|5.009005|5.004000|p
+sv_magicext||5.007003|
+sv_magic|||
+sv_mortalcopy|||
+sv_ncmp|||
+sv_newmortal|||
+sv_newref|||
+sv_nolocking||5.007003|
+sv_nosharing||5.007003|
+sv_nounlocking|||
+sv_nv||5.005000|
+sv_peek||5.005000|
+sv_pos_b2u_midway|||
+sv_pos_b2u||5.006000|
+sv_pos_u2b_cached|||
+sv_pos_u2b_forwards|||n
+sv_pos_u2b_midway|||n
+sv_pos_u2b||5.006000|
+sv_pvbyten_force||5.006000|
+sv_pvbyten||5.006000|
+sv_pvbyte||5.006000|
+sv_pvn_force_flags|5.007002||p
+sv_pvn_force|||
+sv_pvn_nomg|5.007003||p
+sv_pvn|||
+sv_pvutf8n_force||5.006000|
+sv_pvutf8n||5.006000|
+sv_pvutf8||5.006000|
+sv_pv||5.006000|
+sv_recode_to_utf8||5.007003|
+sv_reftype|||
+sv_release_COW|||
+sv_replace|||
+sv_report_used|||
+sv_reset|||
+sv_rvweaken||5.006000|
+sv_setiv_mg|5.004050||p
+sv_setiv|||
+sv_setnv_mg|5.006000||p
+sv_setnv|||
+sv_setpv_mg|5.004050||p
+sv_setpvf_mg_nocontext|||pvn
+sv_setpvf_mg|5.006000|5.004000|pv
+sv_setpvf_nocontext|||vn
+sv_setpvf||5.004000|v
+sv_setpviv_mg||5.008001|
+sv_setpviv||5.008001|
+sv_setpvn_mg|5.004050||p
+sv_setpvn|||
+sv_setpvs|5.009004||p
+sv_setpv|||
+sv_setref_iv|||
+sv_setref_nv|||
+sv_setref_pvn|||
+sv_setref_pv|||
+sv_setref_uv||5.007001|
+sv_setsv_cow|||
+sv_setsv_flags||5.007002|
+sv_setsv_mg|5.004050||p
+sv_setsv_nomg|5.007002||p
+sv_setsv|||
+sv_setuv_mg|5.004050||p
+sv_setuv|5.004000||p
+sv_tainted||5.004000|
+sv_taint||5.004000|
+sv_true||5.005000|
+sv_unglob|||
+sv_uni_display||5.007003|
+sv_unmagic|||
+sv_unref_flags||5.007001|
+sv_unref|||
+sv_untaint||5.004000|
+sv_upgrade|||
+sv_usepvn_flags||5.009004|
+sv_usepvn_mg|5.004050||p
+sv_usepvn|||
+sv_utf8_decode||5.006000|
+sv_utf8_downgrade||5.006000|
+sv_utf8_encode||5.006000|
+sv_utf8_upgrade_flags||5.007002|
+sv_utf8_upgrade||5.007001|
+sv_uv|5.005000||p
+sv_vcatpvf_mg|5.006000|5.004000|p
+sv_vcatpvfn||5.004000|
+sv_vcatpvf|5.006000|5.004000|p
+sv_vsetpvf_mg|5.006000|5.004000|p
+sv_vsetpvfn||5.004000|
+sv_vsetpvf|5.006000|5.004000|p
+sv_xmlpeek|||
+svtype|||
+swallow_bom|||
+swash_fetch||5.007002|
+swash_get|||
+swash_init||5.006000|
+sys_intern_clear|||
+sys_intern_dup|||
+sys_intern_init|||
+taint_env|||
+taint_proper|||
+tmps_grow||5.006000|
+toLOWER|||
+toUPPER|||
+to_byte_substr|||
+to_uni_fold||5.007003|
+to_uni_lower_lc||5.006000|
+to_uni_lower||5.007003|
+to_uni_title_lc||5.006000|
+to_uni_title||5.007003|
+to_uni_upper_lc||5.006000|
+to_uni_upper||5.007003|
+to_utf8_case||5.007003|
+to_utf8_fold||5.007003|
+to_utf8_lower||5.007003|
+to_utf8_substr|||
+to_utf8_title||5.007003|
+to_utf8_upper||5.007003|
+token_free|||
+token_getmad|||
+tokenize_use|||
+tokeq|||
+tokereport|||
+too_few_arguments|||
+too_many_arguments|||
+uiv_2buf|||n
+unlnk|||
+unpack_rec|||
+unpack_str||5.007003|
+unpackstring||5.008001|
+unshare_hek_or_pvn|||
+unshare_hek|||
+unsharepvn||5.004000|
+unwind_handler_stack|||
+update_debugger_info|||
+upg_version||5.009005|
+usage|||
+utf16_to_utf8_reversed||5.006001|
+utf16_to_utf8||5.006001|
+utf8_distance||5.006000|
+utf8_hop||5.006000|
+utf8_length||5.007001|
+utf8_mg_pos_cache_update|||
+utf8_to_bytes||5.006001|
+utf8_to_uvchr||5.007001|
+utf8_to_uvuni||5.007001|
+utf8n_to_uvchr|||
+utf8n_to_uvuni||5.007001|
+utilize|||
+uvchr_to_utf8_flags||5.007003|
+uvchr_to_utf8|||
+uvuni_to_utf8_flags||5.007003|
+uvuni_to_utf8||5.007001|
+validate_suid|||
+varname|||
+vcmp||5.009000|
+vcroak||5.006000|
+vdeb||5.007003|
+vdie_common|||
+vdie_croak_common|||
+vdie|||
+vform||5.006000|
+visit|||
+vivify_defelem|||
+vivify_ref|||
+vload_module|5.006000||p
+vmess||5.006000|
+vnewSVpvf|5.006000|5.004000|p
+vnormal||5.009002|
+vnumify||5.009000|
+vstringify||5.009000|
+vverify||5.009003|
+vwarner||5.006000|
+vwarn||5.006000|
+wait4pid|||
+warn_nocontext|||vn
+warner_nocontext|||vn
+warner|5.006000|5.004000|pv
+warn|||v
+watch|||
+whichsig|||
+write_no_mem|||
+write_to_stderr|||
+xmldump_all|||
+xmldump_attr|||
+xmldump_eval|||
+xmldump_form|||
+xmldump_indent|||v
+xmldump_packsubs|||
+xmldump_sub|||
+xmldump_vindent|||
+yyerror|||
+yylex|||
+yyparse|||
+yywarn|||
+);
+
+if (exists $opt{'list-unsupported'}) {
+ my $f;
+ for $f (sort { lc $a cmp lc $b } keys %API) {
+ next unless $API{$f}{todo};
+ print "$f ", '.'x(40-length($f)), " ", format_version($API{$f}{todo}), "\n";
+ }
+ exit 0;
+}
+
+# Scan for possible replacement candidates
+
+my(%replace, %need, %hints, %warnings, %depends);
+my $replace = 0;
+my($hint, $define, $function);
+
+sub find_api
+{
+ my $code = shift;
+ $code =~ s{
+ / (?: \*[^*]*\*+(?:[^$ccs][^*]*\*+)* / | /[^\r\n]*)
+ | "[^"\\]*(?:\\.[^"\\]*)*"
+ | '[^'\\]*(?:\\.[^'\\]*)*' }{}egsx;
+ grep { exists $API{$_} } $code =~ /(\w+)/mg;
+}
+
+while (<DATA>) {
+ if ($hint) {
+ my $h = $hint->[0] eq 'Hint' ? \%hints : \%warnings;
+ if (m{^\s*\*\s(.*?)\s*$}) {
+ for (@{$hint->[1]}) {
+ $h->{$_} ||= ''; # suppress warning with older perls
+ $h->{$_} .= "$1\n";
+ }
+ }
+ else { undef $hint }
+ }
+
+ $hint = [$1, [split /,?\s+/, $2]]
+ if m{^\s*$rccs\s+(Hint|Warning):\s+(\w+(?:,?\s+\w+)*)\s*$};
+
+ if ($define) {
+ if ($define->[1] =~ /\\$/) {
+ $define->[1] .= $_;
+ }
+ else {
+ if (exists $API{$define->[0]} && $define->[1] !~ /^DPPP_\(/) {
+ my @n = find_api($define->[1]);
+ push @{$depends{$define->[0]}}, @n if @n
+ }
+ undef $define;
+ }
+ }
+
+ $define = [$1, $2] if m{^\s*#\s*define\s+(\w+)(?:\([^)]*\))?\s+(.*)};
+
+ if ($function) {
+ if (/^}/) {
+ if (exists $API{$function->[0]}) {
+ my @n = find_api($function->[1]);
+ push @{$depends{$function->[0]}}, @n if @n
+ }
+ undef $define;
+ }
+ else {
+ $function->[1] .= $_;
+ }
+ }
+
+ $function = [$1, ''] if m{^DPPP_\(my_(\w+)\)};
+
+ $replace = $1 if m{^\s*$rccs\s+Replace:\s+(\d+)\s+$rcce\s*$};
+ $replace{$2} = $1 if $replace and m{^\s*#\s*define\s+(\w+)(?:\([^)]*\))?\s+(\w+)};
+ $replace{$2} = $1 if m{^\s*#\s*define\s+(\w+)(?:\([^)]*\))?\s+(\w+).*$rccs\s+Replace\s+$rcce};
+ $replace{$1} = $2 if m{^\s*$rccs\s+Replace (\w+) with (\w+)\s+$rcce\s*$};
+
+ if (m{^\s*$rccs\s+(\w+)\s+depends\s+on\s+(\w+(\s*,\s*\w+)*)\s+$rcce\s*$}) {
+ push @{$depends{$1}}, map { s/\s+//g; $_ } split /,/, $2;
+ }
+
+ $need{$1} = 1 if m{^#if\s+defined\(NEED_(\w+)(?:_GLOBAL)?\)};
+}
+
+for (values %depends) {
+ my %s;
+ $_ = [sort grep !$s{$_}++, @$_];
+}
+
+if (exists $opt{'api-info'}) {
+ my $f;
+ my $count = 0;
+ my $match = $opt{'api-info'} =~ m!^/(.*)/$! ? $1 : "^\Q$opt{'api-info'}\E\$";
+ for $f (sort { lc $a cmp lc $b } keys %API) {
+ next unless $f =~ /$match/;
+ print "\n=== $f ===\n\n";
+ my $info = 0;
+ if ($API{$f}{base} || $API{$f}{todo}) {
+ my $base = format_version($API{$f}{base} || $API{$f}{todo});
+ print "Supported at least starting from perl-$base.\n";
+ $info++;
+ }
+ if ($API{$f}{provided}) {
+ my $todo = $API{$f}{todo} ? format_version($API{$f}{todo}) : "5.003";
+ print "Support by $ppport provided back to perl-$todo.\n";
+ print "Support needs to be explicitly requested by NEED_$f.\n" if exists $need{$f};
+ print "Depends on: ", join(', ', @{$depends{$f}}), ".\n" if exists $depends{$f};
+ print "\n$hints{$f}" if exists $hints{$f};
+ print "\nWARNING:\n$warnings{$f}" if exists $warnings{$f};
+ $info++;
+ }
+ print "No portability information available.\n" unless $info;
+ $count++;
+ }
+ $count or print "Found no API matching '$opt{'api-info'}'.";
+ print "\n";
+ exit 0;
+}
+
+if (exists $opt{'list-provided'}) {
+ my $f;
+ for $f (sort { lc $a cmp lc $b } keys %API) {
+ next unless $API{$f}{provided};
+ my @flags;
+ push @flags, 'explicit' if exists $need{$f};
+ push @flags, 'depend' if exists $depends{$f};
+ push @flags, 'hint' if exists $hints{$f};
+ push @flags, 'warning' if exists $warnings{$f};
+ my $flags = @flags ? ' ['.join(', ', @flags).']' : '';
+ print "$f$flags\n";
+ }
+ exit 0;
+}
+
+my @files;
+my @srcext = qw( .xs .c .h .cc .cpp -c.inc -xs.inc );
+my $srcext = join '|', map { quotemeta $_ } @srcext;
+
+if (@ARGV) {
+ my %seen;
+ for (@ARGV) {
+ if (-e) {
+ if (-f) {
+ push @files, $_ unless $seen{$_}++;
+ }
+ else { warn "'$_' is not a file.\n" }
+ }
+ else {
+ my @new = grep { -f } glob $_
+ or warn "'$_' does not exist.\n";
+ push @files, grep { !$seen{$_}++ } @new;
+ }
+ }
+}
+else {
+ eval {
+ require File::Find;
+ File::Find::find(sub {
+ $File::Find::name =~ /($srcext)$/i
+ and push @files, $File::Find::name;
+ }, '.');
+ };
+ if ($@) {
+ @files = map { glob "*$_" } @srcext;
+ }
+}
+
+if (!@ARGV || $opt{filter}) {
+ my(@in, @out);
+ my %xsc = map { /(.*)\.xs$/ ? ("$1.c" => 1, "$1.cc" => 1) : () } @files;
+ for (@files) {
+ my $out = exists $xsc{$_} || /\b\Q$ppport\E$/i || !/($srcext)$/i;
+ push @{ $out ? \@out : \@in }, $_;
+ }
+ if (@ARGV && @out) {
+ warning("Skipping the following files (use --nofilter to avoid this):\n| ", join "\n| ", @out);
+ }
+ @files = @in;
+}
+
+die "No input files given!\n" unless @files;
+
+my(%files, %global, %revreplace);
+%revreplace = reverse %replace;
+my $filename;
+my $patch_opened = 0;
+
+for $filename (@files) {
+ unless (open IN, "<$filename") {
+ warn "Unable to read from $filename: $!\n";
+ next;
+ }
+
+ info("Scanning $filename ...");
+
+ my $c = do { local $/; <IN> };
+ close IN;
+
+ my %file = (orig => $c, changes => 0);
+
+ # Temporarily remove C/XS comments and strings from the code
+ my @ccom;
+
+ $c =~ s{
+ ( ^$HS*\#$HS*include\b[^\r\n]+\b(?:\Q$ppport\E|XSUB\.h)\b[^\r\n]*
+ | ^$HS*\#$HS*(?:define|elif|if(?:def)?)\b[^\r\n]* )
+ | ( ^$HS*\#[^\r\n]*
+ | "[^"\\]*(?:\\.[^"\\]*)*"
+ | '[^'\\]*(?:\\.[^'\\]*)*'
+ | / (?: \*[^*]*\*+(?:[^$ccs][^*]*\*+)* / | /[^\r\n]* ) )
+ }{ defined $2 and push @ccom, $2;
+ defined $1 ? $1 : "$ccs$#ccom$cce" }mgsex;
+
+ $file{ccom} = \@ccom;
+ $file{code} = $c;
+ $file{has_inc_ppport} = $c =~ /^$HS*#$HS*include[^\r\n]+\b\Q$ppport\E\b/m;
+
+ my $func;
+
+ for $func (keys %API) {
+ my $match = $func;
+ $match .= "|$revreplace{$func}" if exists $revreplace{$func};
+ if ($c =~ /\b(?:Perl_)?($match)\b/) {
+ $file{uses_replace}{$1}++ if exists $revreplace{$func} && $1 eq $revreplace{$func};
+ $file{uses_Perl}{$func}++ if $c =~ /\bPerl_$func\b/;
+ if (exists $API{$func}{provided}) {
+ $file{uses_provided}{$func}++;
+ if (!exists $API{$func}{base} || $API{$func}{base} > $opt{'compat-version'}) {
+ $file{uses}{$func}++;
+ my @deps = rec_depend($func);
+ if (@deps) {
+ $file{uses_deps}{$func} = \@deps;
+ for (@deps) {
+ $file{uses}{$_} = 0 unless exists $file{uses}{$_};
+ }
+ }
+ for ($func, @deps) {
+ $file{needs}{$_} = 'static' if exists $need{$_};
+ }
+ }
+ }
+ if (exists $API{$func}{todo} && $API{$func}{todo} > $opt{'compat-version'}) {
+ if ($c =~ /\b$func\b/) {
+ $file{uses_todo}{$func}++;
+ }
+ }
+ }
+ }
+
+ while ($c =~ /^$HS*#$HS*define$HS+(NEED_(\w+?)(_GLOBAL)?)\b/mg) {
+ if (exists $need{$2}) {
+ $file{defined $3 ? 'needed_global' : 'needed_static'}{$2}++;
+ }
+ else { warning("Possibly wrong #define $1 in $filename") }
+ }
+
+ for (qw(uses needs uses_todo needed_global needed_static)) {
+ for $func (keys %{$file{$_}}) {
+ push @{$global{$_}{$func}}, $filename;
+ }
+ }
+
+ $files{$filename} = \%file;
+}
+
+# Globally resolve NEED_'s
+my $need;
+for $need (keys %{$global{needs}}) {
+ if (@{$global{needs}{$need}} > 1) {
+ my @targets = @{$global{needs}{$need}};
+ my @t = grep $files{$_}{needed_global}{$need}, @targets;
+ @targets = @t if @t;
+ @t = grep /\.xs$/i, @targets;
+ @targets = @t if @t;
+ my $target = shift @targets;
+ $files{$target}{needs}{$need} = 'global';
+ for (@{$global{needs}{$need}}) {
+ $files{$_}{needs}{$need} = 'extern' if $_ ne $target;
+ }
+ }
+}
+
+for $filename (@files) {
+ exists $files{$filename} or next;
+
+ info("=== Analyzing $filename ===");
+
+ my %file = %{$files{$filename}};
+ my $func;
+ my $c = $file{code};
+ my $warnings = 0;
+
+ for $func (sort keys %{$file{uses_Perl}}) {
+ if ($API{$func}{varargs}) {
+ unless ($API{$func}{nothxarg}) {
+ my $changes = ($c =~ s{\b(Perl_$func\s*\(\s*)(?!aTHX_?)(\)|[^\s)]*\))}
+ { $1 . ($2 eq ')' ? 'aTHX' : 'aTHX_ ') . $2 }ge);
+ if ($changes) {
+ warning("Doesn't pass interpreter argument aTHX to Perl_$func");
+ $file{changes} += $changes;
+ }
+ }
+ }
+ else {
+ warning("Uses Perl_$func instead of $func");
+ $file{changes} += ($c =~ s{\bPerl_$func(\s*)\((\s*aTHX_?)?\s*}
+ {$func$1(}g);
+ }
+ }
+
+ for $func (sort keys %{$file{uses_replace}}) {
+ warning("Uses $func instead of $replace{$func}");
+ $file{changes} += ($c =~ s/\b$func\b/$replace{$func}/g);
+ }
+
+ for $func (sort keys %{$file{uses_provided}}) {
+ if ($file{uses}{$func}) {
+ if (exists $file{uses_deps}{$func}) {
+ diag("Uses $func, which depends on ", join(', ', @{$file{uses_deps}{$func}}));
+ }
+ else {
+ diag("Uses $func");
+ }
+ }
+ $warnings += hint($func);
+ }
+
+ unless ($opt{quiet}) {
+ for $func (sort keys %{$file{uses_todo}}) {
+ print "*** WARNING: Uses $func, which may not be portable below perl ",
+ format_version($API{$func}{todo}), ", even with '$ppport'\n";
+ $warnings++;
+ }
+ }
+
+ for $func (sort keys %{$file{needed_static}}) {
+ my $message = '';
+ if (not exists $file{uses}{$func}) {
+ $message = "No need to define NEED_$func if $func is never used";
+ }
+ elsif (exists $file{needs}{$func} && $file{needs}{$func} ne 'static') {
+ $message = "No need to define NEED_$func when already needed globally";
+ }
+ if ($message) {
+ diag($message);
+ $file{changes} += ($c =~ s/^$HS*#$HS*define$HS+NEED_$func\b.*$LF//mg);
+ }
+ }
+
+ for $func (sort keys %{$file{needed_global}}) {
+ my $message = '';
+ if (not exists $global{uses}{$func}) {
+ $message = "No need to define NEED_${func}_GLOBAL if $func is never used";
+ }
+ elsif (exists $file{needs}{$func}) {
+ if ($file{needs}{$func} eq 'extern') {
+ $message = "No need to define NEED_${func}_GLOBAL when already needed globally";
+ }
+ elsif ($file{needs}{$func} eq 'static') {
+ $message = "No need to define NEED_${func}_GLOBAL when only used in this file";
+ }
+ }
+ if ($message) {
+ diag($message);
+ $file{changes} += ($c =~ s/^$HS*#$HS*define$HS+NEED_${func}_GLOBAL\b.*$LF//mg);
+ }
+ }
+
+ $file{needs_inc_ppport} = keys %{$file{uses}};
+
+ if ($file{needs_inc_ppport}) {
+ my $pp = '';
+
+ for $func (sort keys %{$file{needs}}) {
+ my $type = $file{needs}{$func};
+ next if $type eq 'extern';
+ my $suffix = $type eq 'global' ? '_GLOBAL' : '';
+ unless (exists $file{"needed_$type"}{$func}) {
+ if ($type eq 'global') {
+ diag("Files [@{$global{needs}{$func}}] need $func, adding global request");
+ }
+ else {
+ diag("File needs $func, adding static request");
+ }
+ $pp .= "#define NEED_$func$suffix\n";
+ }
+ }
+
+ if ($pp && ($c =~ s/^(?=$HS*#$HS*define$HS+NEED_\w+)/$pp/m)) {
+ $pp = '';
+ $file{changes}++;
+ }
+
+ unless ($file{has_inc_ppport}) {
+ diag("Needs to include '$ppport'");
+ $pp .= qq(#include "$ppport"\n)
+ }
+
+ if ($pp) {
+ $file{changes} += ($c =~ s/^($HS*#$HS*define$HS+NEED_\w+.*?)^/$1$pp/ms)
+ || ($c =~ s/^(?=$HS*#$HS*include.*\Q$ppport\E)/$pp/m)
+ || ($c =~ s/^($HS*#$HS*include.*XSUB.*\s*?)^/$1$pp/m)
+ || ($c =~ s/^/$pp/);
+ }
+ }
+ else {
+ if ($file{has_inc_ppport}) {
+ diag("No need to include '$ppport'");
+ $file{changes} += ($c =~ s/^$HS*?#$HS*include.*\Q$ppport\E.*?$LF//m);
+ }
+ }
+
+ # put back in our C comments
+ my $ix;
+ my $cppc = 0;
+ my @ccom = @{$file{ccom}};
+ for $ix (0 .. $#ccom) {
+ if (!$opt{cplusplus} && $ccom[$ix] =~ s!^//!!) {
+ $cppc++;
+ $file{changes} += $c =~ s/$rccs$ix$rcce/$ccs$ccom[$ix] $cce/;
+ }
+ else {
+ $c =~ s/$rccs$ix$rcce/$ccom[$ix]/;
+ }
+ }
+
+ if ($cppc) {
+ my $s = $cppc != 1 ? 's' : '';
+ warning("Uses $cppc C++ style comment$s, which is not portable");
+ }
+
+ my $s = $warnings != 1 ? 's' : '';
+ my $warn = $warnings ? " ($warnings warning$s)" : '';
+ info("Analysis completed$warn");
+
+ if ($file{changes}) {
+ if (exists $opt{copy}) {
+ my $newfile = "$filename$opt{copy}";
+ if (-e $newfile) {
+ error("'$newfile' already exists, refusing to write copy of '$filename'");
+ }
+ else {
+ local *F;
+ if (open F, ">$newfile") {
+ info("Writing copy of '$filename' with changes to '$newfile'");
+ print F $c;
+ close F;
+ }
+ else {
+ error("Cannot open '$newfile' for writing: $!");
+ }
+ }
+ }
+ elsif (exists $opt{patch} || $opt{changes}) {
+ if (exists $opt{patch}) {
+ unless ($patch_opened) {
+ if (open PATCH, ">$opt{patch}") {
+ $patch_opened = 1;
+ }
+ else {
+ error("Cannot open '$opt{patch}' for writing: $!");
+ delete $opt{patch};
+ $opt{changes} = 1;
+ goto fallback;
+ }
+ }
+ mydiff(\*PATCH, $filename, $c);
+ }
+ else {
+fallback:
+ info("Suggested changes:");
+ mydiff(\*STDOUT, $filename, $c);
+ }
+ }
+ else {
+ my $s = $file{changes} == 1 ? '' : 's';
+ info("$file{changes} potentially required change$s detected");
+ }
+ }
+ else {
+ info("Looks good");
+ }
+}
+
+close PATCH if $patch_opened;
+
+exit 0;
+
+
+sub try_use { eval "use @_;"; return $@ eq '' }
+
+sub mydiff
+{
+ local *F = shift;
+ my($file, $str) = @_;
+ my $diff;
+
+ if (exists $opt{diff}) {
+ $diff = run_diff($opt{diff}, $file, $str);
+ }
+
+ if (!defined $diff and try_use('Text::Diff')) {
+ $diff = Text::Diff::diff($file, \$str, { STYLE => 'Unified' });
+ $diff = <<HEADER . $diff;
+--- $file
++++ $file.patched
+HEADER
+ }
+
+ if (!defined $diff) {
+ $diff = run_diff('diff -u', $file, $str);
+ }
+
+ if (!defined $diff) {
+ $diff = run_diff('diff', $file, $str);
+ }
+
+ if (!defined $diff) {
+ error("Cannot generate a diff. Please install Text::Diff or use --copy.");
+ return;
+ }
+
+ print F $diff;
+}
+
+sub run_diff
+{
+ my($prog, $file, $str) = @_;
+ my $tmp = 'dppptemp';
+ my $suf = 'aaa';
+ my $diff = '';
+ local *F;
+
+ while (-e "$tmp.$suf") { $suf++ }
+ $tmp = "$tmp.$suf";
+
+ if (open F, ">$tmp") {
+ print F $str;
+ close F;
+
+ if (open F, "$prog $file $tmp |") {
+ while (<F>) {
+ s/\Q$tmp\E/$file.patched/;
+ $diff .= $_;
+ }
+ close F;
+ unlink $tmp;
+ return $diff;
+ }
+
+ unlink $tmp;
+ }
+ else {
+ error("Cannot open '$tmp' for writing: $!");
+ }
+
+ return undef;
+}
+
+sub rec_depend
+{
+ my($func, $seen) = @_;
+ return () unless exists $depends{$func};
+ $seen = {%{$seen||{}}};
+ return () if $seen->{$func}++;
+ my %s;
+ grep !$s{$_}++, map { ($_, rec_depend($_, $seen)) } @{$depends{$func}};
+}
+
+sub parse_version
+{
+ my $ver = shift;
+
+ if ($ver =~ /^(\d+)\.(\d+)\.(\d+)$/) {
+ return ($1, $2, $3);
+ }
+ elsif ($ver !~ /^\d+\.[\d_]+$/) {
+ die "cannot parse version '$ver'\n";
+ }
+
+ $ver =~ s/_//g;
+ $ver =~ s/$/000000/;
+
+ my($r,$v,$s) = $ver =~ /(\d+)\.(\d{3})(\d{3})/;
+
+ $v = int $v;
+ $s = int $s;
+
+ if ($r < 5 || ($r == 5 && $v < 6)) {
+ if ($s % 10) {
+ die "cannot parse version '$ver'\n";
+ }
+ }
+
+ return ($r, $v, $s);
+}
+
+sub format_version
+{
+ my $ver = shift;
+
+ $ver =~ s/$/000000/;
+ my($r,$v,$s) = $ver =~ /(\d+)\.(\d{3})(\d{3})/;
+
+ $v = int $v;
+ $s = int $s;
+
+ if ($r < 5 || ($r == 5 && $v < 6)) {
+ if ($s % 10) {
+ die "invalid version '$ver'\n";
+ }
+ $s /= 10;
+
+ $ver = sprintf "%d.%03d", $r, $v;
+ $s > 0 and $ver .= sprintf "_%02d", $s;
+
+ return $ver;
+ }
+
+ return sprintf "%d.%d.%d", $r, $v, $s;
+}
+
+sub info
+{
+ $opt{quiet} and return;
+ print @_, "\n";
+}
+
+sub diag
+{
+ $opt{quiet} and return;
+ $opt{diag} and print @_, "\n";
+}
+
+sub warning
+{
+ $opt{quiet} and return;
+ print "*** ", @_, "\n";
+}
+
+sub error
+{
+ print "*** ERROR: ", @_, "\n";
+}
+
+my %given_hints;
+my %given_warnings;
+sub hint
+{
+ $opt{quiet} and return;
+ my $func = shift;
+ my $rv = 0;
+ if (exists $warnings{$func} && !$given_warnings{$func}++) {
+ my $warn = $warnings{$func};
+ $warn =~ s!^!*** !mg;
+ print "*** WARNING: $func\n", $warn;
+ $rv++;
+ }
+ if ($opt{hints} && exists $hints{$func} && !$given_hints{$func}++) {
+ my $hint = $hints{$func};
+ $hint =~ s/^/ /mg;
+ print " --- hint for $func ---\n", $hint;
+ }
+ $rv;
+}
+
+sub usage
+{
+ my($usage) = do { local(@ARGV,$/)=($0); <> } =~ /^=head\d$HS+SYNOPSIS\s*^(.*?)\s*^=/ms;
+ my %M = ( 'I' => '*' );
+ $usage =~ s/^\s*perl\s+\S+/$^X $0/;
+ $usage =~ s/([A-Z])<([^>]+)>/$M{$1}$2$M{$1}/g;
+
+ print <<ENDUSAGE;
+
+Usage: $usage
+
+See perldoc $0 for details.
+
+ENDUSAGE
+
+ exit 2;
+}
+
+sub strip
+{
+ my $self = do { local(@ARGV,$/)=($0); <> };
+ my($copy) = $self =~ /^=head\d\s+COPYRIGHT\s*^(.*?)^=\w+/ms;
+ $copy =~ s/^(?=\S+)/ /gms;
+ $self =~ s/^$HS+Do NOT edit.*?(?=^-)/$copy/ms;
+ $self =~ s/^SKIP.*(?=^__DATA__)/SKIP
+if (\@ARGV && \$ARGV[0] eq '--unstrip') {
+ eval { require Devel::PPPort };
+ \$@ and die "Cannot require Devel::PPPort, please install.\\n";
+ if (\$Devel::PPPort::VERSION < $VERSION) {
+ die "$0 was originally generated with Devel::PPPort $VERSION.\\n"
+ . "Your Devel::PPPort is only version \$Devel::PPPort::VERSION.\\n"
+ . "Please install a newer version, or --unstrip will not work.\\n";
+ }
+ Devel::PPPort::WriteFile(\$0);
+ exit 0;
+}
+print <<END;
+
+Sorry, but this is a stripped version of \$0.
+
+To be able to use its original script and doc functionality,
+please try to regenerate this file using:
+
+ \$^X \$0 --unstrip
+
+END
+/ms;
+ my($pl, $c) = $self =~ /(.*^__DATA__)(.*)/ms;
+ $c =~ s{
+ / (?: \*[^*]*\*+(?:[^$ccs][^*]*\*+)* / | /[^\r\n]*)
+ | ( "[^"\\]*(?:\\.[^"\\]*)*"
+ | '[^'\\]*(?:\\.[^'\\]*)*' )
+ | ($HS+) }{ defined $2 ? ' ' : ($1 || '') }gsex;
+ $c =~ s!\s+$!!mg;
+ $c =~ s!^$LF!!mg;
+ $c =~ s!^\s*#\s*!#!mg;
+ $c =~ s!^\s+!!mg;
+
+ open OUT, ">$0" or die "cannot strip $0: $!\n";
+ print OUT "$pl$c\n";
+
+ exit 0;
+}
+
+__DATA__
+*/
+
+#ifndef _P_P_PORTABILITY_H_
+#define _P_P_PORTABILITY_H_
+
+#ifndef DPPP_NAMESPACE
+# define DPPP_NAMESPACE DPPP_
+#endif
+
+#define DPPP_CAT2(x,y) CAT2(x,y)
+#define DPPP_(name) DPPP_CAT2(DPPP_NAMESPACE, name)
+
+#ifndef PERL_REVISION
+# if !defined(__PATCHLEVEL_H_INCLUDED__) && !(defined(PATCHLEVEL) && defined(SUBVERSION))
+# define PERL_PATCHLEVEL_H_IMPLICIT
+# include <patchlevel.h>
+# endif
+# if !(defined(PERL_VERSION) || (defined(SUBVERSION) && defined(PATCHLEVEL)))
+# include <could_not_find_Perl_patchlevel.h>
+# endif
+# ifndef PERL_REVISION
+# define PERL_REVISION (5)
+ /* Replace: 1 */
+# define PERL_VERSION PATCHLEVEL
+# define PERL_SUBVERSION SUBVERSION
+ /* Replace PERL_PATCHLEVEL with PERL_VERSION */
+ /* Replace: 0 */
+# endif
+#endif
+
+#define _dpppDEC2BCD(dec) ((((dec)/100)<<8)|((((dec)%100)/10)<<4)|((dec)%10))
+#define PERL_BCDVERSION ((_dpppDEC2BCD(PERL_REVISION)<<24)|(_dpppDEC2BCD(PERL_VERSION)<<12)|_dpppDEC2BCD(PERL_SUBVERSION))
+
+/* It is very unlikely that anyone will try to use this with Perl 6
+ (or greater), but who knows.
+ */
+#if PERL_REVISION != 5
+# error ppport.h only works with Perl version 5
+#endif /* PERL_REVISION != 5 */
+
+#ifdef I_LIMITS
+# include <limits.h>
+#endif
+
+#ifndef PERL_UCHAR_MIN
+# define PERL_UCHAR_MIN ((unsigned char)0)
+#endif
+
+#ifndef PERL_UCHAR_MAX
+# ifdef UCHAR_MAX
+# define PERL_UCHAR_MAX ((unsigned char)UCHAR_MAX)
+# else
+# ifdef MAXUCHAR
+# define PERL_UCHAR_MAX ((unsigned char)MAXUCHAR)
+# else
+# define PERL_UCHAR_MAX ((unsigned char)~(unsigned)0)
+# endif
+# endif
+#endif
+
+#ifndef PERL_USHORT_MIN
+# define PERL_USHORT_MIN ((unsigned short)0)
+#endif
+
+#ifndef PERL_USHORT_MAX
+# ifdef USHORT_MAX
+# define PERL_USHORT_MAX ((unsigned short)USHORT_MAX)
+# else
+# ifdef MAXUSHORT
+# define PERL_USHORT_MAX ((unsigned short)MAXUSHORT)
+# else
+# ifdef USHRT_MAX
+# define PERL_USHORT_MAX ((unsigned short)USHRT_MAX)
+# else
+# define PERL_USHORT_MAX ((unsigned short)~(unsigned)0)
+# endif
+# endif
+# endif
+#endif
+
+#ifndef PERL_SHORT_MAX
+# ifdef SHORT_MAX
+# define PERL_SHORT_MAX ((short)SHORT_MAX)
+# else
+# ifdef MAXSHORT /* Often used in <values.h> */
+# define PERL_SHORT_MAX ((short)MAXSHORT)
+# else
+# ifdef SHRT_MAX
+# define PERL_SHORT_MAX ((short)SHRT_MAX)
+# else
+# define PERL_SHORT_MAX ((short) (PERL_USHORT_MAX >> 1))
+# endif
+# endif
+# endif
+#endif
+
+#ifndef PERL_SHORT_MIN
+# ifdef SHORT_MIN
+# define PERL_SHORT_MIN ((short)SHORT_MIN)
+# else
+# ifdef MINSHORT
+# define PERL_SHORT_MIN ((short)MINSHORT)
+# else
+# ifdef SHRT_MIN
+# define PERL_SHORT_MIN ((short)SHRT_MIN)
+# else
+# define PERL_SHORT_MIN (-PERL_SHORT_MAX - ((3 & -1) == 3))
+# endif
+# endif
+# endif
+#endif
+
+#ifndef PERL_UINT_MAX
+# ifdef UINT_MAX
+# define PERL_UINT_MAX ((unsigned int)UINT_MAX)
+# else
+# ifdef MAXUINT
+# define PERL_UINT_MAX ((unsigned int)MAXUINT)
+# else
+# define PERL_UINT_MAX (~(unsigned int)0)
+# endif
+# endif
+#endif
+
+#ifndef PERL_UINT_MIN
+# define PERL_UINT_MIN ((unsigned int)0)
+#endif
+
+#ifndef PERL_INT_MAX
+# ifdef INT_MAX
+# define PERL_INT_MAX ((int)INT_MAX)
+# else
+# ifdef MAXINT /* Often used in <values.h> */
+# define PERL_INT_MAX ((int)MAXINT)
+# else
+# define PERL_INT_MAX ((int)(PERL_UINT_MAX >> 1))
+# endif
+# endif
+#endif
+
+#ifndef PERL_INT_MIN
+# ifdef INT_MIN
+# define PERL_INT_MIN ((int)INT_MIN)
+# else
+# ifdef MININT
+# define PERL_INT_MIN ((int)MININT)
+# else
+# define PERL_INT_MIN (-PERL_INT_MAX - ((3 & -1) == 3))
+# endif
+# endif
+#endif
+
+#ifndef PERL_ULONG_MAX
+# ifdef ULONG_MAX
+# define PERL_ULONG_MAX ((unsigned long)ULONG_MAX)
+# else
+# ifdef MAXULONG
+# define PERL_ULONG_MAX ((unsigned long)MAXULONG)
+# else
+# define PERL_ULONG_MAX (~(unsigned long)0)
+# endif
+# endif
+#endif
+
+#ifndef PERL_ULONG_MIN
+# define PERL_ULONG_MIN ((unsigned long)0L)
+#endif
+
+#ifndef PERL_LONG_MAX
+# ifdef LONG_MAX
+# define PERL_LONG_MAX ((long)LONG_MAX)
+# else
+# ifdef MAXLONG
+# define PERL_LONG_MAX ((long)MAXLONG)
+# else
+# define PERL_LONG_MAX ((long) (PERL_ULONG_MAX >> 1))
+# endif
+# endif
+#endif
+
+#ifndef PERL_LONG_MIN
+# ifdef LONG_MIN
+# define PERL_LONG_MIN ((long)LONG_MIN)
+# else
+# ifdef MINLONG
+# define PERL_LONG_MIN ((long)MINLONG)
+# else
+# define PERL_LONG_MIN (-PERL_LONG_MAX - ((3 & -1) == 3))
+# endif
+# endif
+#endif
+
+#if defined(HAS_QUAD) && (defined(convex) || defined(uts))
+# ifndef PERL_UQUAD_MAX
+# ifdef ULONGLONG_MAX
+# define PERL_UQUAD_MAX ((unsigned long long)ULONGLONG_MAX)
+# else
+# ifdef MAXULONGLONG
+# define PERL_UQUAD_MAX ((unsigned long long)MAXULONGLONG)
+# else
+# define PERL_UQUAD_MAX (~(unsigned long long)0)
+# endif
+# endif
+# endif
+
+# ifndef PERL_UQUAD_MIN
+# define PERL_UQUAD_MIN ((unsigned long long)0L)
+# endif
+
+# ifndef PERL_QUAD_MAX
+# ifdef LONGLONG_MAX
+# define PERL_QUAD_MAX ((long long)LONGLONG_MAX)
+# else
+# ifdef MAXLONGLONG
+# define PERL_QUAD_MAX ((long long)MAXLONGLONG)
+# else
+# define PERL_QUAD_MAX ((long long) (PERL_UQUAD_MAX >> 1))
+# endif
+# endif
+# endif
+
+# ifndef PERL_QUAD_MIN
+# ifdef LONGLONG_MIN
+# define PERL_QUAD_MIN ((long long)LONGLONG_MIN)
+# else
+# ifdef MINLONGLONG
+# define PERL_QUAD_MIN ((long long)MINLONGLONG)
+# else
+# define PERL_QUAD_MIN (-PERL_QUAD_MAX - ((3 & -1) == 3))
+# endif
+# endif
+# endif
+#endif
+
+/* This is based on code from 5.003 perl.h */
+#ifdef HAS_QUAD
+# ifdef cray
+#ifndef IVTYPE
+# define IVTYPE int
+#endif
+
+#ifndef IV_MIN
+# define IV_MIN PERL_INT_MIN
+#endif
+
+#ifndef IV_MAX
+# define IV_MAX PERL_INT_MAX
+#endif
+
+#ifndef UV_MIN
+# define UV_MIN PERL_UINT_MIN
+#endif
+
+#ifndef UV_MAX
+# define UV_MAX PERL_UINT_MAX
+#endif
+
+# ifdef INTSIZE
+#ifndef IVSIZE
+# define IVSIZE INTSIZE
+#endif
+
+# endif
+# else
+# if defined(convex) || defined(uts)
+#ifndef IVTYPE
+# define IVTYPE long long
+#endif
+
+#ifndef IV_MIN
+# define IV_MIN PERL_QUAD_MIN
+#endif
+
+#ifndef IV_MAX
+# define IV_MAX PERL_QUAD_MAX
+#endif
+
+#ifndef UV_MIN
+# define UV_MIN PERL_UQUAD_MIN
+#endif
+
+#ifndef UV_MAX
+# define UV_MAX PERL_UQUAD_MAX
+#endif
+
+# ifdef LONGLONGSIZE
+#ifndef IVSIZE
+# define IVSIZE LONGLONGSIZE
+#endif
+
+# endif
+# else
+#ifndef IVTYPE
+# define IVTYPE long
+#endif
+
+#ifndef IV_MIN
+# define IV_MIN PERL_LONG_MIN
+#endif
+
+#ifndef IV_MAX
+# define IV_MAX PERL_LONG_MAX
+#endif
+
+#ifndef UV_MIN
+# define UV_MIN PERL_ULONG_MIN
+#endif
+
+#ifndef UV_MAX
+# define UV_MAX PERL_ULONG_MAX
+#endif
+
+# ifdef LONGSIZE
+#ifndef IVSIZE
+# define IVSIZE LONGSIZE
+#endif
+
+# endif
+# endif
+# endif
+#ifndef IVSIZE
+# define IVSIZE 8
+#endif
+
+#ifndef PERL_QUAD_MIN
+# define PERL_QUAD_MIN IV_MIN
+#endif
+
+#ifndef PERL_QUAD_MAX
+# define PERL_QUAD_MAX IV_MAX
+#endif
+
+#ifndef PERL_UQUAD_MIN
+# define PERL_UQUAD_MIN UV_MIN
+#endif
+
+#ifndef PERL_UQUAD_MAX
+# define PERL_UQUAD_MAX UV_MAX
+#endif
+
+#else
+#ifndef IVTYPE
+# define IVTYPE long
+#endif
+
+#ifndef IV_MIN
+# define IV_MIN PERL_LONG_MIN
+#endif
+
+#ifndef IV_MAX
+# define IV_MAX PERL_LONG_MAX
+#endif
+
+#ifndef UV_MIN
+# define UV_MIN PERL_ULONG_MIN
+#endif
+
+#ifndef UV_MAX
+# define UV_MAX PERL_ULONG_MAX
+#endif
+
+#endif
+
+#ifndef IVSIZE
+# ifdef LONGSIZE
+# define IVSIZE LONGSIZE
+# else
+# define IVSIZE 4 /* A bold guess, but the best we can make. */
+# endif
+#endif
+#ifndef UVTYPE
+# define UVTYPE unsigned IVTYPE
+#endif
+
+#ifndef UVSIZE
+# define UVSIZE IVSIZE
+#endif
+#ifndef sv_setuv
+# define sv_setuv(sv, uv) \
+ STMT_START { \
+ UV TeMpUv = uv; \
+ if (TeMpUv <= IV_MAX) \
+ sv_setiv(sv, TeMpUv); \
+ else \
+ sv_setnv(sv, (double)TeMpUv); \
+ } STMT_END
+#endif
+#ifndef newSVuv
+# define newSVuv(uv) ((uv) <= IV_MAX ? newSViv((IV)uv) : newSVnv((NV)uv))
+#endif
+#ifndef sv_2uv
+# define sv_2uv(sv) ((PL_Sv = (sv)), (UV) (SvNOK(PL_Sv) ? SvNV(PL_Sv) : sv_2nv(PL_Sv)))
+#endif
+
+#ifndef SvUVX
+# define SvUVX(sv) ((UV)SvIVX(sv))
+#endif
+
+#ifndef SvUVXx
+# define SvUVXx(sv) SvUVX(sv)
+#endif
+
+#ifndef SvUV
+# define SvUV(sv) (SvIOK(sv) ? SvUVX(sv) : sv_2uv(sv))
+#endif
+
+#ifndef SvUVx
+# define SvUVx(sv) ((PL_Sv = (sv)), SvUV(PL_Sv))
+#endif
+
+/* Hint: sv_uv
+ * Always use the SvUVx() macro instead of sv_uv().
+ */
+#ifndef sv_uv
+# define sv_uv(sv) SvUVx(sv)
+#endif
+
+#if !defined(SvUOK) && defined(SvIOK_UV)
+# define SvUOK(sv) SvIOK_UV(sv)
+#endif
+#ifndef XST_mUV
+# define XST_mUV(i,v) (ST(i) = sv_2mortal(newSVuv(v)) )
+#endif
+
+#ifndef XSRETURN_UV
+# define XSRETURN_UV(v) STMT_START { XST_mUV(0,v); XSRETURN(1); } STMT_END
+#endif
+#ifndef PUSHu
+# define PUSHu(u) STMT_START { sv_setuv(TARG, (UV)(u)); PUSHTARG; } STMT_END
+#endif
+
+#ifndef XPUSHu
+# define XPUSHu(u) STMT_START { sv_setuv(TARG, (UV)(u)); XPUSHTARG; } STMT_END
+#endif
+
+#ifdef HAS_MEMCMP
+#ifndef memNE
+# define memNE(s1,s2,l) (memcmp(s1,s2,l))
+#endif
+
+#ifndef memEQ
+# define memEQ(s1,s2,l) (!memcmp(s1,s2,l))
+#endif
+
+#else
+#ifndef memNE
+# define memNE(s1,s2,l) (bcmp(s1,s2,l))
+#endif
+
+#ifndef memEQ
+# define memEQ(s1,s2,l) (!bcmp(s1,s2,l))
+#endif
+
+#endif
+#ifndef MoveD
+# define MoveD(s,d,n,t) memmove((char*)(d),(char*)(s), (n) * sizeof(t))
+#endif
+
+#ifndef CopyD
+# define CopyD(s,d,n,t) memcpy((char*)(d),(char*)(s), (n) * sizeof(t))
+#endif
+
+#ifdef HAS_MEMSET
+#ifndef ZeroD
+# define ZeroD(d,n,t) memzero((char*)(d), (n) * sizeof(t))
+#endif
+
+#else
+#ifndef ZeroD
+# define ZeroD(d,n,t) ((void)memzero((char*)(d), (n) * sizeof(t)), d)
+#endif
+
+#endif
+#ifndef PoisonWith
+# define PoisonWith(d,n,t,b) (void)memset((char*)(d), (U8)(b), (n) * sizeof(t))
+#endif
+
+#ifndef PoisonNew
+# define PoisonNew(d,n,t) PoisonWith(d,n,t,0xAB)
+#endif
+
+#ifndef PoisonFree
+# define PoisonFree(d,n,t) PoisonWith(d,n,t,0xEF)
+#endif
+
+#ifndef Poison
+# define Poison(d,n,t) PoisonFree(d,n,t)
+#endif
+#ifndef Newx
+# define Newx(v,n,t) New(0,v,n,t)
+#endif
+
+#ifndef Newxc
+# define Newxc(v,n,t,c) Newc(0,v,n,t,c)
+#endif
+
+#ifndef Newxz
+# define Newxz(v,n,t) Newz(0,v,n,t)
+#endif
+
+#ifndef PERL_UNUSED_DECL
+# ifdef HASATTRIBUTE
+# if (defined(__GNUC__) && defined(__cplusplus)) || defined(__INTEL_COMPILER)
+# define PERL_UNUSED_DECL
+# else
+# define PERL_UNUSED_DECL __attribute__((unused))
+# endif
+# else
+# define PERL_UNUSED_DECL
+# endif
+#endif
+
+#ifndef PERL_UNUSED_ARG
+# if defined(lint) && defined(S_SPLINT_S) /* www.splint.org */
+# include <note.h>
+# define PERL_UNUSED_ARG(x) NOTE(ARGUNUSED(x))
+# else
+# define PERL_UNUSED_ARG(x) ((void)x)
+# endif
+#endif
+
+#ifndef PERL_UNUSED_VAR
+# define PERL_UNUSED_VAR(x) ((void)x)
+#endif
+
+#ifndef PERL_UNUSED_CONTEXT
+# ifdef USE_ITHREADS
+# define PERL_UNUSED_CONTEXT PERL_UNUSED_ARG(my_perl)
+# else
+# define PERL_UNUSED_CONTEXT
+# endif
+#endif
+#ifndef NOOP
+# define NOOP /*EMPTY*/(void)0
+#endif
+
+#ifndef dNOOP
+# define dNOOP extern int /*@unused@*/ Perl___notused PERL_UNUSED_DECL
+#endif
+
+#ifndef NVTYPE
+# if defined(USE_LONG_DOUBLE) && defined(HAS_LONG_DOUBLE)
+# define NVTYPE long double
+# else
+# define NVTYPE double
+# endif
+typedef NVTYPE NV;
+#endif
+
+#ifndef INT2PTR
+
+# if (IVSIZE == PTRSIZE) && (UVSIZE == PTRSIZE)
+# define PTRV UV
+# define INT2PTR(any,d) (any)(d)
+# else
+# if PTRSIZE == LONGSIZE
+# define PTRV unsigned long
+# else
+# define PTRV unsigned
+# endif
+# define INT2PTR(any,d) (any)(PTRV)(d)
+# endif
+
+# define NUM2PTR(any,d) (any)(PTRV)(d)
+# define PTR2IV(p) INT2PTR(IV,p)
+# define PTR2UV(p) INT2PTR(UV,p)
+# define PTR2NV(p) NUM2PTR(NV,p)
+
+# if PTRSIZE == LONGSIZE
+# define PTR2ul(p) (unsigned long)(p)
+# else
+# define PTR2ul(p) INT2PTR(unsigned long,p)
+# endif
+
+#endif /* !INT2PTR */
+
+#undef START_EXTERN_C
+#undef END_EXTERN_C
+#undef EXTERN_C
+#ifdef __cplusplus
+# define START_EXTERN_C extern "C" {
+# define END_EXTERN_C }
+# define EXTERN_C extern "C"
+#else
+# define START_EXTERN_C
+# define END_EXTERN_C
+# define EXTERN_C extern
+#endif
+
+#if defined(PERL_GCC_PEDANTIC)
+# ifndef PERL_GCC_BRACE_GROUPS_FORBIDDEN
+# define PERL_GCC_BRACE_GROUPS_FORBIDDEN
+# endif
+#endif
+
+#if defined(__GNUC__) && !defined(PERL_GCC_BRACE_GROUPS_FORBIDDEN) && !defined(__cplusplus)
+# ifndef PERL_USE_GCC_BRACE_GROUPS
+# define PERL_USE_GCC_BRACE_GROUPS
+# endif
+#endif
+
+#undef STMT_START
+#undef STMT_END
+#ifdef PERL_USE_GCC_BRACE_GROUPS
+# define STMT_START (void)( /* gcc supports ``({ STATEMENTS; })'' */
+# define STMT_END )
+#else
+# if defined(VOIDFLAGS) && (VOIDFLAGS) && (defined(sun) || defined(__sun__)) && !defined(__GNUC__)
+# define STMT_START if (1)
+# define STMT_END else (void)0
+# else
+# define STMT_START do
+# define STMT_END while (0)
+# endif
+#endif
+#ifndef boolSV
+# define boolSV(b) ((b) ? &PL_sv_yes : &PL_sv_no)
+#endif
+
+/* DEFSV appears first in 5.004_56 */
+#ifndef DEFSV
+# define DEFSV GvSV(PL_defgv)
+#endif
+
+#ifndef SAVE_DEFSV
+# define SAVE_DEFSV SAVESPTR(GvSV(PL_defgv))
+#endif
+
+/* Older perls (<=5.003) lack AvFILLp */
+#ifndef AvFILLp
+# define AvFILLp AvFILL
+#endif
+#ifndef ERRSV
+# define ERRSV get_sv("@",FALSE)
+#endif
+#ifndef newSVpvn
+# define newSVpvn(data,len) ((data) \
+ ? ((len) ? newSVpv((data), (len)) : newSVpv("", 0)) \
+ : newSV(0))
+#endif
+
+/* Hint: gv_stashpvn
+ * This function's backport doesn't support the length parameter, but
+ * rather ignores it. Portability can only be ensured if the length
+ * parameter is used for speed reasons, but the length can always be
+ * correctly computed from the string argument.
+ */
+#ifndef gv_stashpvn
+# define gv_stashpvn(str,len,create) gv_stashpv(str,create)
+#endif
+
+/* Replace: 1 */
+#ifndef get_cv
+# define get_cv perl_get_cv
+#endif
+
+#ifndef get_sv
+# define get_sv perl_get_sv
+#endif
+
+#ifndef get_av
+# define get_av perl_get_av
+#endif
+
+#ifndef get_hv
+# define get_hv perl_get_hv
+#endif
+
+/* Replace: 0 */
+#ifndef dUNDERBAR
+# define dUNDERBAR dNOOP
+#endif
+
+#ifndef UNDERBAR
+# define UNDERBAR DEFSV
+#endif
+#ifndef dAX
+# define dAX I32 ax = MARK - PL_stack_base + 1
+#endif
+
+#ifndef dITEMS
+# define dITEMS I32 items = SP - MARK
+#endif
+#ifndef dXSTARG
+# define dXSTARG SV * targ = sv_newmortal()
+#endif
+#ifndef dAXMARK
+# define dAXMARK I32 ax = POPMARK; \
+ register SV ** const mark = PL_stack_base + ax++
+#endif
+#ifndef XSprePUSH
+# define XSprePUSH (sp = PL_stack_base + ax - 1)
+#endif
+
+#if (PERL_BCDVERSION < 0x5005000)
+# undef XSRETURN
+# define XSRETURN(off) \
+ STMT_START { \
+ PL_stack_sp = PL_stack_base + ax + ((off) - 1); \
+ return; \
+ } STMT_END
+#endif
+#ifndef PERL_ABS
+# define PERL_ABS(x) ((x) < 0 ? -(x) : (x))
+#endif
+#ifndef dVAR
+# define dVAR dNOOP
+#endif
+#ifndef SVf
+# define SVf "_"
+#endif
+#ifndef UTF8_MAXBYTES
+# define UTF8_MAXBYTES UTF8_MAXLEN
+#endif
+#ifndef PERL_HASH
+# define PERL_HASH(hash,str,len) \
+ STMT_START { \
+ const char *s_PeRlHaSh = str; \
+ I32 i_PeRlHaSh = len; \
+ U32 hash_PeRlHaSh = 0; \
+ while (i_PeRlHaSh--) \
+ hash_PeRlHaSh = hash_PeRlHaSh * 33 + *s_PeRlHaSh++; \
+ (hash) = hash_PeRlHaSh; \
+ } STMT_END
+#endif
+
+#ifndef PERL_SIGNALS_UNSAFE_FLAG
+
+#define PERL_SIGNALS_UNSAFE_FLAG 0x0001
+
+#if (PERL_BCDVERSION < 0x5008000)
+# define D_PPP_PERL_SIGNALS_INIT PERL_SIGNALS_UNSAFE_FLAG
+#else
+# define D_PPP_PERL_SIGNALS_INIT 0
+#endif
+
+#if defined(NEED_PL_signals)
+static U32 DPPP_(my_PL_signals) = D_PPP_PERL_SIGNALS_INIT;
+#elif defined(NEED_PL_signals_GLOBAL)
+U32 DPPP_(my_PL_signals) = D_PPP_PERL_SIGNALS_INIT;
+#else
+extern U32 DPPP_(my_PL_signals);
+#endif
+#define PL_signals DPPP_(my_PL_signals)
+
+#endif
+
+/* Hint: PL_ppaddr
+ * Calling an op via PL_ppaddr requires passing a context argument
+ * for threaded builds. Since the context argument is different for
+ * 5.005 perls, you can use aTHXR (supplied by ppport.h), which will
+ * automatically be defined as the correct argument.
+ */
+
+#if (PERL_BCDVERSION <= 0x5005005)
+/* Replace: 1 */
+# define PL_ppaddr ppaddr
+# define PL_no_modify no_modify
+/* Replace: 0 */
+#endif
+
+#if (PERL_BCDVERSION <= 0x5004005)
+/* Replace: 1 */
+# define PL_DBsignal DBsignal
+# define PL_DBsingle DBsingle
+# define PL_DBsub DBsub
+# define PL_DBtrace DBtrace
+# define PL_Sv Sv
+# define PL_compiling compiling
+# define PL_copline copline
+# define PL_curcop curcop
+# define PL_curstash curstash
+# define PL_debstash debstash
+# define PL_defgv defgv
+# define PL_diehook diehook
+# define PL_dirty dirty
+# define PL_dowarn dowarn
+# define PL_errgv errgv
+# define PL_expect expect
+# define PL_hexdigit hexdigit
+# define PL_hints hints
+# define PL_laststatval laststatval
+# define PL_na na
+# define PL_perl_destruct_level perl_destruct_level
+# define PL_perldb perldb
+# define PL_rsfp_filters rsfp_filters
+# define PL_rsfp rsfp
+# define PL_stack_base stack_base
+# define PL_stack_sp stack_sp
+# define PL_statcache statcache
+# define PL_stdingv stdingv
+# define PL_sv_arenaroot sv_arenaroot
+# define PL_sv_no sv_no
+# define PL_sv_undef sv_undef
+# define PL_sv_yes sv_yes
+# define PL_tainted tainted
+# define PL_tainting tainting
+/* Replace: 0 */
+#endif
+
+/* Warning: PL_expect, PL_copline, PL_rsfp, PL_rsfp_filters
+ * Do not use this variable. It is internal to the perl parser
+ * and may change or even be removed in the future. Note that
+ * as of perl 5.9.5 you cannot assign to this variable anymore.
+ */
+
+/* TODO: cannot assign to these vars; is it worth fixing? */
+#if (PERL_BCDVERSION >= 0x5009005)
+# define PL_expect (PL_parser ? PL_parser->expect : 0)
+# define PL_copline (PL_parser ? PL_parser->copline : 0)
+# define PL_rsfp (PL_parser ? PL_parser->rsfp : (PerlIO *) 0)
+# define PL_rsfp_filters (PL_parser ? PL_parser->rsfp_filters : (AV *) 0)
+#endif
+#ifndef dTHR
+# define dTHR dNOOP
+#endif
+#ifndef dTHX
+# define dTHX dNOOP
+#endif
+
+#ifndef dTHXa
+# define dTHXa(x) dNOOP
+#endif
+#ifndef pTHX
+# define pTHX void
+#endif
+
+#ifndef pTHX_
+# define pTHX_
+#endif
+
+#ifndef aTHX
+# define aTHX
+#endif
+
+#ifndef aTHX_
+# define aTHX_
+#endif
+
+#if (PERL_BCDVERSION < 0x5006000)
+# ifdef USE_THREADS
+# define aTHXR thr
+# define aTHXR_ thr,
+# else
+# define aTHXR
+# define aTHXR_
+# endif
+# define dTHXR dTHR
+#else
+# define aTHXR aTHX
+# define aTHXR_ aTHX_
+# define dTHXR dTHX
+#endif
+#ifndef dTHXoa
+# define dTHXoa(x) dTHXa(x)
+#endif
+#ifndef PUSHmortal
+# define PUSHmortal PUSHs(sv_newmortal())
+#endif
+
+#ifndef mPUSHp
+# define mPUSHp(p,l) sv_setpvn_mg(PUSHmortal, (p), (l))
+#endif
+
+#ifndef mPUSHn
+# define mPUSHn(n) sv_setnv_mg(PUSHmortal, (NV)(n))
+#endif
+
+#ifndef mPUSHi
+# define mPUSHi(i) sv_setiv_mg(PUSHmortal, (IV)(i))
+#endif
+
+#ifndef mPUSHu
+# define mPUSHu(u) sv_setuv_mg(PUSHmortal, (UV)(u))
+#endif
+#ifndef XPUSHmortal
+# define XPUSHmortal XPUSHs(sv_newmortal())
+#endif
+
+#ifndef mXPUSHp
+# define mXPUSHp(p,l) STMT_START { EXTEND(sp,1); sv_setpvn_mg(PUSHmortal, (p), (l)); } STMT_END
+#endif
+
+#ifndef mXPUSHn
+# define mXPUSHn(n) STMT_START { EXTEND(sp,1); sv_setnv_mg(PUSHmortal, (NV)(n)); } STMT_END
+#endif
+
+#ifndef mXPUSHi
+# define mXPUSHi(i) STMT_START { EXTEND(sp,1); sv_setiv_mg(PUSHmortal, (IV)(i)); } STMT_END
+#endif
+
+#ifndef mXPUSHu
+# define mXPUSHu(u) STMT_START { EXTEND(sp,1); sv_setuv_mg(PUSHmortal, (UV)(u)); } STMT_END
+#endif
+
+/* Replace: 1 */
+#ifndef call_sv
+# define call_sv perl_call_sv
+#endif
+
+#ifndef call_pv
+# define call_pv perl_call_pv
+#endif
+
+#ifndef call_argv
+# define call_argv perl_call_argv
+#endif
+
+#ifndef call_method
+# define call_method perl_call_method
+#endif
+#ifndef eval_sv
+# define eval_sv perl_eval_sv
+#endif
+#ifndef PERL_LOADMOD_DENY
+# define PERL_LOADMOD_DENY 0x1
+#endif
+
+#ifndef PERL_LOADMOD_NOIMPORT
+# define PERL_LOADMOD_NOIMPORT 0x2
+#endif
+
+#ifndef PERL_LOADMOD_IMPORT_OPS
+# define PERL_LOADMOD_IMPORT_OPS 0x4
+#endif
+
+/* Replace: 0 */
+
+/* Replace perl_eval_pv with eval_pv */
+
+#ifndef eval_pv
+#if defined(NEED_eval_pv)
+static SV* DPPP_(my_eval_pv)(char *p, I32 croak_on_error);
+static
+#else
+extern SV* DPPP_(my_eval_pv)(char *p, I32 croak_on_error);
+#endif
+
+#ifdef eval_pv
+# undef eval_pv
+#endif
+#define eval_pv(a,b) DPPP_(my_eval_pv)(aTHX_ a,b)
+#define Perl_eval_pv DPPP_(my_eval_pv)
+
+#if defined(NEED_eval_pv) || defined(NEED_eval_pv_GLOBAL)
+
+SV*
+DPPP_(my_eval_pv)(char *p, I32 croak_on_error)
+{
+ dSP;
+ SV* sv = newSVpv(p, 0);
+
+ PUSHMARK(sp);
+ eval_sv(sv, G_SCALAR);
+ SvREFCNT_dec(sv);
+
+ SPAGAIN;
+ sv = POPs;
+ PUTBACK;
+
+ if (croak_on_error && SvTRUE(GvSV(errgv)))
+ croak(SvPVx(GvSV(errgv), na));
+
+ return sv;
+}
+
+#endif
+#endif
+
+#ifndef vload_module
+#if defined(NEED_vload_module)
+static void DPPP_(my_vload_module)(U32 flags, SV *name, SV *ver, va_list *args);
+static
+#else
+extern void DPPP_(my_vload_module)(U32 flags, SV *name, SV *ver, va_list *args);
+#endif
+
+#ifdef vload_module
+# undef vload_module
+#endif
+#define vload_module(a,b,c,d) DPPP_(my_vload_module)(aTHX_ a,b,c,d)
+#define Perl_vload_module DPPP_(my_vload_module)
+
+#if defined(NEED_vload_module) || defined(NEED_vload_module_GLOBAL)
+
+void
+DPPP_(my_vload_module)(U32 flags, SV *name, SV *ver, va_list *args)
+{
+ dTHR;
+ dVAR;
+ OP *veop, *imop;
+
+ OP * const modname = newSVOP(OP_CONST, 0, name);
+ /* 5.005 has a somewhat hacky force_normal that doesn't croak on
+ SvREADONLY() if PL_compling is true. Current perls take care in
+ ck_require() to correctly turn off SvREADONLY before calling
+ force_normal_flags(). This seems a better fix than fudging PL_compling
+ */
+ SvREADONLY_off(((SVOP*)modname)->op_sv);
+ modname->op_private |= OPpCONST_BARE;
+ if (ver) {
+ veop = newSVOP(OP_CONST, 0, ver);
+ }
+ else
+ veop = NULL;
+ if (flags & PERL_LOADMOD_NOIMPORT) {
+ imop = sawparens(newNULLLIST());
+ }
+ else if (flags & PERL_LOADMOD_IMPORT_OPS) {
+ imop = va_arg(*args, OP*);
+ }
+ else {
+ SV *sv;
+ imop = NULL;
+ sv = va_arg(*args, SV*);
+ while (sv) {
+ imop = append_elem(OP_LIST, imop, newSVOP(OP_CONST, 0, sv));
+ sv = va_arg(*args, SV*);
+ }
+ }
+ {
+ const line_t ocopline = PL_copline;
+ COP * const ocurcop = PL_curcop;
+ const int oexpect = PL_expect;
+
+#if (PERL_BCDVERSION >= 0x5004000)
+ utilize(!(flags & PERL_LOADMOD_DENY), start_subparse(FALSE, 0),
+ veop, modname, imop);
+#else
+ utilize(!(flags & PERL_LOADMOD_DENY), start_subparse(),
+ modname, imop);
+#endif
+ PL_expect = oexpect;
+ PL_copline = ocopline;
+ PL_curcop = ocurcop;
+ }
+}
+
+#endif
+#endif
+
+#ifndef load_module
+#if defined(NEED_load_module)
+static void DPPP_(my_load_module)(U32 flags, SV *name, SV *ver, ...);
+static
+#else
+extern void DPPP_(my_load_module)(U32 flags, SV *name, SV *ver, ...);
+#endif
+
+#ifdef load_module
+# undef load_module
+#endif
+#define load_module DPPP_(my_load_module)
+#define Perl_load_module DPPP_(my_load_module)
+
+#if defined(NEED_load_module) || defined(NEED_load_module_GLOBAL)
+
+void
+DPPP_(my_load_module)(U32 flags, SV *name, SV *ver, ...)
+{
+ va_list args;
+ va_start(args, ver);
+ vload_module(flags, name, ver, &args);
+ va_end(args);
+}
+
+#endif
+#endif
+#ifndef newRV_inc
+# define newRV_inc(sv) newRV(sv) /* Replace */
+#endif
+
+#ifndef newRV_noinc
+#if defined(NEED_newRV_noinc)
+static SV * DPPP_(my_newRV_noinc)(SV *sv);
+static
+#else
+extern SV * DPPP_(my_newRV_noinc)(SV *sv);
+#endif
+
+#ifdef newRV_noinc
+# undef newRV_noinc
+#endif
+#define newRV_noinc(a) DPPP_(my_newRV_noinc)(aTHX_ a)
+#define Perl_newRV_noinc DPPP_(my_newRV_noinc)
+
+#if defined(NEED_newRV_noinc) || defined(NEED_newRV_noinc_GLOBAL)
+SV *
+DPPP_(my_newRV_noinc)(SV *sv)
+{
+ SV *rv = (SV *)newRV(sv);
+ SvREFCNT_dec(sv);
+ return rv;
+}
+#endif
+#endif
+
+/* Hint: newCONSTSUB
+ * Returns a CV* as of perl-5.7.1. This return value is not supported
+ * by Devel::PPPort.
+ */
+
+/* newCONSTSUB from IO.xs is in the core starting with 5.004_63 */
+#if (PERL_BCDVERSION < 0x5004063) && (PERL_BCDVERSION != 0x5004005)
+#if defined(NEED_newCONSTSUB)
+static void DPPP_(my_newCONSTSUB)(HV *stash, const char *name, SV *sv);
+static
+#else
+extern void DPPP_(my_newCONSTSUB)(HV *stash, const char *name, SV *sv);
+#endif
+
+#ifdef newCONSTSUB
+# undef newCONSTSUB
+#endif
+#define newCONSTSUB(a,b,c) DPPP_(my_newCONSTSUB)(aTHX_ a,b,c)
+#define Perl_newCONSTSUB DPPP_(my_newCONSTSUB)
+
+#if defined(NEED_newCONSTSUB) || defined(NEED_newCONSTSUB_GLOBAL)
+
+void
+DPPP_(my_newCONSTSUB)(HV *stash, const char *name, SV *sv)
+{
+ U32 oldhints = PL_hints;
+ HV *old_cop_stash = PL_curcop->cop_stash;
+ HV *old_curstash = PL_curstash;
+ line_t oldline = PL_curcop->cop_line;
+ PL_curcop->cop_line = PL_copline;
+
+ PL_hints &= ~HINT_BLOCK_SCOPE;
+ if (stash)
+ PL_curstash = PL_curcop->cop_stash = stash;
+
+ newSUB(
+
+#if (PERL_BCDVERSION < 0x5003022)
+ start_subparse(),
+#elif (PERL_BCDVERSION == 0x5003022)
+ start_subparse(0),
+#else /* 5.003_23 onwards */
+ start_subparse(FALSE, 0),
+#endif
+
+ newSVOP(OP_CONST, 0, newSVpv((char *) name, 0)),
+ newSVOP(OP_CONST, 0, &PL_sv_no), /* SvPV(&PL_sv_no) == "" -- GMB */
+ newSTATEOP(0, Nullch, newSVOP(OP_CONST, 0, sv))
+ );
+
+ PL_hints = oldhints;
+ PL_curcop->cop_stash = old_cop_stash;
+ PL_curstash = old_curstash;
+ PL_curcop->cop_line = oldline;
+}
+#endif
+#endif
+
+/*
+ * Boilerplate macros for initializing and accessing interpreter-local
+ * data from C. All statics in extensions should be reworked to use
+ * this, if you want to make the extension thread-safe. See ext/re/re.xs
+ * for an example of the use of these macros.
+ *
+ * Code that uses these macros is responsible for the following:
+ * 1. #define MY_CXT_KEY to a unique string, e.g. "DynaLoader_guts"
+ * 2. Declare a typedef named my_cxt_t that is a structure that contains
+ * all the data that needs to be interpreter-local.
+ * 3. Use the START_MY_CXT macro after the declaration of my_cxt_t.
+ * 4. Use the MY_CXT_INIT macro such that it is called exactly once
+ * (typically put in the BOOT: section).
+ * 5. Use the members of the my_cxt_t structure everywhere as
+ * MY_CXT.member.
+ * 6. Use the dMY_CXT macro (a declaration) in all the functions that
+ * access MY_CXT.
+ */
+
+#if defined(MULTIPLICITY) || defined(PERL_OBJECT) || \
+ defined(PERL_CAPI) || defined(PERL_IMPLICIT_CONTEXT)
+
+#ifndef START_MY_CXT
+
+/* This must appear in all extensions that define a my_cxt_t structure,
+ * right after the definition (i.e. at file scope). The non-threads
+ * case below uses it to declare the data as static. */
+#define START_MY_CXT
+
+#if (PERL_BCDVERSION < 0x5004068)
+/* Fetches the SV that keeps the per-interpreter data. */
+#define dMY_CXT_SV \
+ SV *my_cxt_sv = get_sv(MY_CXT_KEY, FALSE)
+#else /* >= perl5.004_68 */
+#define dMY_CXT_SV \
+ SV *my_cxt_sv = *hv_fetch(PL_modglobal, MY_CXT_KEY, \
+ sizeof(MY_CXT_KEY)-1, TRUE)
+#endif /* < perl5.004_68 */
+
+/* This declaration should be used within all functions that use the
+ * interpreter-local data. */
+#define dMY_CXT \
+ dMY_CXT_SV; \
+ my_cxt_t *my_cxtp = INT2PTR(my_cxt_t*,SvUV(my_cxt_sv))
+
+/* Creates and zeroes the per-interpreter data.
+ * (We allocate my_cxtp in a Perl SV so that it will be released when
+ * the interpreter goes away.) */
+#define MY_CXT_INIT \
+ dMY_CXT_SV; \
+ /* newSV() allocates one more than needed */ \
+ my_cxt_t *my_cxtp = (my_cxt_t*)SvPVX(newSV(sizeof(my_cxt_t)-1));\
+ Zero(my_cxtp, 1, my_cxt_t); \
+ sv_setuv(my_cxt_sv, PTR2UV(my_cxtp))
+
+/* This macro must be used to access members of the my_cxt_t structure.
+ * e.g. MYCXT.some_data */
+#define MY_CXT (*my_cxtp)
+
+/* Judicious use of these macros can reduce the number of times dMY_CXT
+ * is used. Use is similar to pTHX, aTHX etc. */
+#define pMY_CXT my_cxt_t *my_cxtp
+#define pMY_CXT_ pMY_CXT,
+#define _pMY_CXT ,pMY_CXT
+#define aMY_CXT my_cxtp
+#define aMY_CXT_ aMY_CXT,
+#define _aMY_CXT ,aMY_CXT
+
+#endif /* START_MY_CXT */
+
+#ifndef MY_CXT_CLONE
+/* Clones the per-interpreter data. */
+#define MY_CXT_CLONE \
+ dMY_CXT_SV; \
+ my_cxt_t *my_cxtp = (my_cxt_t*)SvPVX(newSV(sizeof(my_cxt_t)-1));\
+ Copy(INT2PTR(my_cxt_t*, SvUV(my_cxt_sv)), my_cxtp, 1, my_cxt_t);\
+ sv_setuv(my_cxt_sv, PTR2UV(my_cxtp))
+#endif
+
+#else /* single interpreter */
+
+#ifndef START_MY_CXT
+
+#define START_MY_CXT static my_cxt_t my_cxt;
+#define dMY_CXT_SV dNOOP
+#define dMY_CXT dNOOP
+#define MY_CXT_INIT NOOP
+#define MY_CXT my_cxt
+
+#define pMY_CXT void
+#define pMY_CXT_
+#define _pMY_CXT
+#define aMY_CXT
+#define aMY_CXT_
+#define _aMY_CXT
+
+#endif /* START_MY_CXT */
+
+#ifndef MY_CXT_CLONE
+#define MY_CXT_CLONE NOOP
+#endif
+
+#endif
+
+#ifndef IVdf
+# if IVSIZE == LONGSIZE
+# define IVdf "ld"
+# define UVuf "lu"
+# define UVof "lo"
+# define UVxf "lx"
+# define UVXf "lX"
+# else
+# if IVSIZE == INTSIZE
+# define IVdf "d"
+# define UVuf "u"
+# define UVof "o"
+# define UVxf "x"
+# define UVXf "X"
+# endif
+# endif
+#endif
+
+#ifndef NVef
+# if defined(USE_LONG_DOUBLE) && defined(HAS_LONG_DOUBLE) && \
+ defined(PERL_PRIfldbl) /* Not very likely, but let's try anyway. */
+# define NVef PERL_PRIeldbl
+# define NVff PERL_PRIfldbl
+# define NVgf PERL_PRIgldbl
+# else
+# define NVef "e"
+# define NVff "f"
+# define NVgf "g"
+# endif
+#endif
+
+#ifndef SvREFCNT_inc
+# ifdef PERL_USE_GCC_BRACE_GROUPS
+# define SvREFCNT_inc(sv) \
+ ({ \
+ SV * const _sv = (SV*)(sv); \
+ if (_sv) \
+ (SvREFCNT(_sv))++; \
+ _sv; \
+ })
+# else
+# define SvREFCNT_inc(sv) \
+ ((PL_Sv=(SV*)(sv)) ? (++(SvREFCNT(PL_Sv)),PL_Sv) : NULL)
+# endif
+#endif
+
+#ifndef SvREFCNT_inc_simple
+# ifdef PERL_USE_GCC_BRACE_GROUPS
+# define SvREFCNT_inc_simple(sv) \
+ ({ \
+ if (sv) \
+ (SvREFCNT(sv))++; \
+ (SV *)(sv); \
+ })
+# else
+# define SvREFCNT_inc_simple(sv) \
+ ((sv) ? (SvREFCNT(sv)++,(SV*)(sv)) : NULL)
+# endif
+#endif
+
+#ifndef SvREFCNT_inc_NN
+# ifdef PERL_USE_GCC_BRACE_GROUPS
+# define SvREFCNT_inc_NN(sv) \
+ ({ \
+ SV * const _sv = (SV*)(sv); \
+ SvREFCNT(_sv)++; \
+ _sv; \
+ })
+# else
+# define SvREFCNT_inc_NN(sv) \
+ (PL_Sv=(SV*)(sv),++(SvREFCNT(PL_Sv)),PL_Sv)
+# endif
+#endif
+
+#ifndef SvREFCNT_inc_void
+# ifdef PERL_USE_GCC_BRACE_GROUPS
+# define SvREFCNT_inc_void(sv) \
+ ({ \
+ SV * const _sv = (SV*)(sv); \
+ if (_sv) \
+ (void)(SvREFCNT(_sv)++); \
+ })
+# else
+# define SvREFCNT_inc_void(sv) \
+ (void)((PL_Sv=(SV*)(sv)) ? ++(SvREFCNT(PL_Sv)) : 0)
+# endif
+#endif
+#ifndef SvREFCNT_inc_simple_void
+# define SvREFCNT_inc_simple_void(sv) STMT_START { if (sv) SvREFCNT(sv)++; } STMT_END
+#endif
+
+#ifndef SvREFCNT_inc_simple_NN
+# define SvREFCNT_inc_simple_NN(sv) (++SvREFCNT(sv), (SV*)(sv))
+#endif
+
+#ifndef SvREFCNT_inc_void_NN
+# define SvREFCNT_inc_void_NN(sv) (void)(++SvREFCNT((SV*)(sv)))
+#endif
+
+#ifndef SvREFCNT_inc_simple_void_NN
+# define SvREFCNT_inc_simple_void_NN(sv) (void)(++SvREFCNT((SV*)(sv)))
+#endif
+
+/* Backwards compatibility stuff... :-( */
+#if !defined(NEED_sv_2pv_flags) && defined(NEED_sv_2pv_nolen)
+# define NEED_sv_2pv_flags
+#endif
+#if !defined(NEED_sv_2pv_flags_GLOBAL) && defined(NEED_sv_2pv_nolen_GLOBAL)
+# define NEED_sv_2pv_flags_GLOBAL
+#endif
+
+/* Hint: sv_2pv_nolen
+ * Use the SvPV_nolen() or SvPV_nolen_const() macros instead of sv_2pv_nolen().
+ */
+#ifndef sv_2pv_nolen
+# define sv_2pv_nolen(sv) SvPV_nolen(sv)
+#endif
+
+#ifdef SvPVbyte
+
+/* Hint: SvPVbyte
+ * Does not work in perl-5.6.1, ppport.h implements a version
+ * borrowed from perl-5.7.3.
+ */
+
+#if (PERL_BCDVERSION < 0x5007000)
+
+#if defined(NEED_sv_2pvbyte)
+static char * DPPP_(my_sv_2pvbyte)(pTHX_ SV * sv, STRLEN * lp);
+static
+#else
+extern char * DPPP_(my_sv_2pvbyte)(pTHX_ SV * sv, STRLEN * lp);
+#endif
+
+#ifdef sv_2pvbyte
+# undef sv_2pvbyte
+#endif
+#define sv_2pvbyte(a,b) DPPP_(my_sv_2pvbyte)(aTHX_ a,b)
+#define Perl_sv_2pvbyte DPPP_(my_sv_2pvbyte)
+
+#if defined(NEED_sv_2pvbyte) || defined(NEED_sv_2pvbyte_GLOBAL)
+
+char *
+DPPP_(my_sv_2pvbyte)(pTHX_ SV *sv, STRLEN *lp)
+{
+ sv_utf8_downgrade(sv,0);
+ return SvPV(sv,*lp);
+}
+
+#endif
+
+/* Hint: sv_2pvbyte
+ * Use the SvPVbyte() macro instead of sv_2pvbyte().
+ */
+
+#undef SvPVbyte
+
+#define SvPVbyte(sv, lp) \
+ ((SvFLAGS(sv) & (SVf_POK|SVf_UTF8)) == (SVf_POK) \
+ ? ((lp = SvCUR(sv)), SvPVX(sv)) : sv_2pvbyte(sv, &lp))
+
+#endif
+
+#else
+
+# define SvPVbyte SvPV
+# define sv_2pvbyte sv_2pv
+
+#endif
+#ifndef sv_2pvbyte_nolen
+# define sv_2pvbyte_nolen(sv) sv_2pv_nolen(sv)
+#endif
+
+/* Hint: sv_pvn
+ * Always use the SvPV() macro instead of sv_pvn().
+ */
+
+/* Hint: sv_pvn_force
+ * Always use the SvPV_force() macro instead of sv_pvn_force().
+ */
+
+/* If these are undefined, they're not handled by the core anyway */
+#ifndef SV_IMMEDIATE_UNREF
+# define SV_IMMEDIATE_UNREF 0
+#endif
+
+#ifndef SV_GMAGIC
+# define SV_GMAGIC 0
+#endif
+
+#ifndef SV_COW_DROP_PV
+# define SV_COW_DROP_PV 0
+#endif
+
+#ifndef SV_UTF8_NO_ENCODING
+# define SV_UTF8_NO_ENCODING 0
+#endif
+
+#ifndef SV_NOSTEAL
+# define SV_NOSTEAL 0
+#endif
+
+#ifndef SV_CONST_RETURN
+# define SV_CONST_RETURN 0
+#endif
+
+#ifndef SV_MUTABLE_RETURN
+# define SV_MUTABLE_RETURN 0
+#endif
+
+#ifndef SV_SMAGIC
+# define SV_SMAGIC 0
+#endif
+
+#ifndef SV_HAS_TRAILING_NUL
+# define SV_HAS_TRAILING_NUL 0
+#endif
+
+#ifndef SV_COW_SHARED_HASH_KEYS
+# define SV_COW_SHARED_HASH_KEYS 0
+#endif
+
+#if (PERL_BCDVERSION < 0x5007002)
+
+#if defined(NEED_sv_2pv_flags)
+static char * DPPP_(my_sv_2pv_flags)(pTHX_ SV * sv, STRLEN * lp, I32 flags);
+static
+#else
+extern char * DPPP_(my_sv_2pv_flags)(pTHX_ SV * sv, STRLEN * lp, I32 flags);
+#endif
+
+#ifdef sv_2pv_flags
+# undef sv_2pv_flags
+#endif
+#define sv_2pv_flags(a,b,c) DPPP_(my_sv_2pv_flags)(aTHX_ a,b,c)
+#define Perl_sv_2pv_flags DPPP_(my_sv_2pv_flags)
+
+#if defined(NEED_sv_2pv_flags) || defined(NEED_sv_2pv_flags_GLOBAL)
+
+char *
+DPPP_(my_sv_2pv_flags)(pTHX_ SV *sv, STRLEN *lp, I32 flags)
+{
+ STRLEN n_a = (STRLEN) flags;
+ return sv_2pv(sv, lp ? lp : &n_a);
+}
+
+#endif
+
+#if defined(NEED_sv_pvn_force_flags)
+static char * DPPP_(my_sv_pvn_force_flags)(pTHX_ SV * sv, STRLEN * lp, I32 flags);
+static
+#else
+extern char * DPPP_(my_sv_pvn_force_flags)(pTHX_ SV * sv, STRLEN * lp, I32 flags);
+#endif
+
+#ifdef sv_pvn_force_flags
+# undef sv_pvn_force_flags
+#endif
+#define sv_pvn_force_flags(a,b,c) DPPP_(my_sv_pvn_force_flags)(aTHX_ a,b,c)
+#define Perl_sv_pvn_force_flags DPPP_(my_sv_pvn_force_flags)
+
+#if defined(NEED_sv_pvn_force_flags) || defined(NEED_sv_pvn_force_flags_GLOBAL)
+
+char *
+DPPP_(my_sv_pvn_force_flags)(pTHX_ SV *sv, STRLEN *lp, I32 flags)
+{
+ STRLEN n_a = (STRLEN) flags;
+ return sv_pvn_force(sv, lp ? lp : &n_a);
+}
+
+#endif
+
+#endif
+#ifndef SvPV_const
+# define SvPV_const(sv, lp) SvPV_flags_const(sv, lp, SV_GMAGIC)
+#endif
+
+#ifndef SvPV_mutable
+# define SvPV_mutable(sv, lp) SvPV_flags_mutable(sv, lp, SV_GMAGIC)
+#endif
+#ifndef SvPV_flags
+# define SvPV_flags(sv, lp, flags) \
+ ((SvFLAGS(sv) & (SVf_POK)) == SVf_POK \
+ ? ((lp = SvCUR(sv)), SvPVX(sv)) : sv_2pv_flags(sv, &lp, flags))
+#endif
+#ifndef SvPV_flags_const
+# define SvPV_flags_const(sv, lp, flags) \
+ ((SvFLAGS(sv) & (SVf_POK)) == SVf_POK \
+ ? ((lp = SvCUR(sv)), SvPVX_const(sv)) : \
+ (const char*) sv_2pv_flags(sv, &lp, flags|SV_CONST_RETURN))
+#endif
+#ifndef SvPV_flags_const_nolen
+# define SvPV_flags_const_nolen(sv, flags) \
+ ((SvFLAGS(sv) & (SVf_POK)) == SVf_POK \
+ ? SvPVX_const(sv) : \
+ (const char*) sv_2pv_flags(sv, 0, flags|SV_CONST_RETURN))
+#endif
+#ifndef SvPV_flags_mutable
+# define SvPV_flags_mutable(sv, lp, flags) \
+ ((SvFLAGS(sv) & (SVf_POK)) == SVf_POK \
+ ? ((lp = SvCUR(sv)), SvPVX_mutable(sv)) : \
+ sv_2pv_flags(sv, &lp, flags|SV_MUTABLE_RETURN))
+#endif
+#ifndef SvPV_force
+# define SvPV_force(sv, lp) SvPV_force_flags(sv, lp, SV_GMAGIC)
+#endif
+
+#ifndef SvPV_force_nolen
+# define SvPV_force_nolen(sv) SvPV_force_flags_nolen(sv, SV_GMAGIC)
+#endif
+
+#ifndef SvPV_force_mutable
+# define SvPV_force_mutable(sv, lp) SvPV_force_flags_mutable(sv, lp, SV_GMAGIC)
+#endif
+
+#ifndef SvPV_force_nomg
+# define SvPV_force_nomg(sv, lp) SvPV_force_flags(sv, lp, 0)
+#endif
+
+#ifndef SvPV_force_nomg_nolen
+# define SvPV_force_nomg_nolen(sv) SvPV_force_flags_nolen(sv, 0)
+#endif
+#ifndef SvPV_force_flags
+# define SvPV_force_flags(sv, lp, flags) \
+ ((SvFLAGS(sv) & (SVf_POK|SVf_THINKFIRST)) == SVf_POK \
+ ? ((lp = SvCUR(sv)), SvPVX(sv)) : sv_pvn_force_flags(sv, &lp, flags))
+#endif
+#ifndef SvPV_force_flags_nolen
+# define SvPV_force_flags_nolen(sv, flags) \
+ ((SvFLAGS(sv) & (SVf_POK|SVf_THINKFIRST)) == SVf_POK \
+ ? SvPVX(sv) : sv_pvn_force_flags(sv, 0, flags))
+#endif
+#ifndef SvPV_force_flags_mutable
+# define SvPV_force_flags_mutable(sv, lp, flags) \
+ ((SvFLAGS(sv) & (SVf_POK|SVf_THINKFIRST)) == SVf_POK \
+ ? ((lp = SvCUR(sv)), SvPVX_mutable(sv)) \
+ : sv_pvn_force_flags(sv, &lp, flags|SV_MUTABLE_RETURN))
+#endif
+#ifndef SvPV_nolen
+# define SvPV_nolen(sv) \
+ ((SvFLAGS(sv) & (SVf_POK)) == SVf_POK \
+ ? SvPVX(sv) : sv_2pv_flags(sv, 0, SV_GMAGIC))
+#endif
+#ifndef SvPV_nolen_const
+# define SvPV_nolen_const(sv) \
+ ((SvFLAGS(sv) & (SVf_POK)) == SVf_POK \
+ ? SvPVX_const(sv) : sv_2pv_flags(sv, 0, SV_GMAGIC|SV_CONST_RETURN))
+#endif
+#ifndef SvPV_nomg
+# define SvPV_nomg(sv, lp) SvPV_flags(sv, lp, 0)
+#endif
+
+#ifndef SvPV_nomg_const
+# define SvPV_nomg_const(sv, lp) SvPV_flags_const(sv, lp, 0)
+#endif
+
+#ifndef SvPV_nomg_const_nolen
+# define SvPV_nomg_const_nolen(sv) SvPV_flags_const_nolen(sv, 0)
+#endif
+#ifndef SvMAGIC_set
+# define SvMAGIC_set(sv, val) \
+ STMT_START { assert(SvTYPE(sv) >= SVt_PVMG); \
+ (((XPVMG*) SvANY(sv))->xmg_magic = (val)); } STMT_END
+#endif
+
+#if (PERL_BCDVERSION < 0x5009003)
+#ifndef SvPVX_const
+# define SvPVX_const(sv) ((const char*) (0 + SvPVX(sv)))
+#endif
+
+#ifndef SvPVX_mutable
+# define SvPVX_mutable(sv) (0 + SvPVX(sv))
+#endif
+#ifndef SvRV_set
+# define SvRV_set(sv, val) \
+ STMT_START { assert(SvTYPE(sv) >= SVt_RV); \
+ (((XRV*) SvANY(sv))->xrv_rv = (val)); } STMT_END
+#endif
+
+#else
+#ifndef SvPVX_const
+# define SvPVX_const(sv) ((const char*)((sv)->sv_u.svu_pv))
+#endif
+
+#ifndef SvPVX_mutable
+# define SvPVX_mutable(sv) ((sv)->sv_u.svu_pv)
+#endif
+#ifndef SvRV_set
+# define SvRV_set(sv, val) \
+ STMT_START { assert(SvTYPE(sv) >= SVt_RV); \
+ ((sv)->sv_u.svu_rv = (val)); } STMT_END
+#endif
+
+#endif
+#ifndef SvSTASH_set
+# define SvSTASH_set(sv, val) \
+ STMT_START { assert(SvTYPE(sv) >= SVt_PVMG); \
+ (((XPVMG*) SvANY(sv))->xmg_stash = (val)); } STMT_END
+#endif
+
+#if (PERL_BCDVERSION < 0x5004000)
+#ifndef SvUV_set
+# define SvUV_set(sv, val) \
+ STMT_START { assert(SvTYPE(sv) == SVt_IV || SvTYPE(sv) >= SVt_PVIV); \
+ (((XPVIV*) SvANY(sv))->xiv_iv = (IV) (val)); } STMT_END
+#endif
+
+#else
+#ifndef SvUV_set
+# define SvUV_set(sv, val) \
+ STMT_START { assert(SvTYPE(sv) == SVt_IV || SvTYPE(sv) >= SVt_PVIV); \
+ (((XPVUV*) SvANY(sv))->xuv_uv = (val)); } STMT_END
+#endif
+
+#endif
+
+#if (PERL_BCDVERSION >= 0x5004000) && !defined(vnewSVpvf)
+#if defined(NEED_vnewSVpvf)
+static SV * DPPP_(my_vnewSVpvf)(pTHX_ const char * pat, va_list * args);
+static
+#else
+extern SV * DPPP_(my_vnewSVpvf)(pTHX_ const char * pat, va_list * args);
+#endif
+
+#ifdef vnewSVpvf
+# undef vnewSVpvf
+#endif
+#define vnewSVpvf(a,b) DPPP_(my_vnewSVpvf)(aTHX_ a,b)
+#define Perl_vnewSVpvf DPPP_(my_vnewSVpvf)
+
+#if defined(NEED_vnewSVpvf) || defined(NEED_vnewSVpvf_GLOBAL)
+
+SV *
+DPPP_(my_vnewSVpvf)(pTHX_ const char *pat, va_list *args)
+{
+ register SV *sv = newSV(0);
+ sv_vsetpvfn(sv, pat, strlen(pat), args, Null(SV**), 0, Null(bool*));
+ return sv;
+}
+
+#endif
+#endif
+
+#if (PERL_BCDVERSION >= 0x5004000) && !defined(sv_vcatpvf)
+# define sv_vcatpvf(sv, pat, args) sv_vcatpvfn(sv, pat, strlen(pat), args, Null(SV**), 0, Null(bool*))
+#endif
+
+#if (PERL_BCDVERSION >= 0x5004000) && !defined(sv_vsetpvf)
+# define sv_vsetpvf(sv, pat, args) sv_vsetpvfn(sv, pat, strlen(pat), args, Null(SV**), 0, Null(bool*))
+#endif
+
+#if (PERL_BCDVERSION >= 0x5004000) && !defined(sv_catpvf_mg)
+#if defined(NEED_sv_catpvf_mg)
+static void DPPP_(my_sv_catpvf_mg)(pTHX_ SV * sv, const char * pat, ...);
+static
+#else
+extern void DPPP_(my_sv_catpvf_mg)(pTHX_ SV * sv, const char * pat, ...);
+#endif
+
+#define Perl_sv_catpvf_mg DPPP_(my_sv_catpvf_mg)
+
+#if defined(NEED_sv_catpvf_mg) || defined(NEED_sv_catpvf_mg_GLOBAL)
+
+void
+DPPP_(my_sv_catpvf_mg)(pTHX_ SV *sv, const char *pat, ...)
+{
+ va_list args;
+ va_start(args, pat);
+ sv_vcatpvfn(sv, pat, strlen(pat), &args, Null(SV**), 0, Null(bool*));
+ SvSETMAGIC(sv);
+ va_end(args);
+}
+
+#endif
+#endif
+
+#ifdef PERL_IMPLICIT_CONTEXT
+#if (PERL_BCDVERSION >= 0x5004000) && !defined(sv_catpvf_mg_nocontext)
+#if defined(NEED_sv_catpvf_mg_nocontext)
+static void DPPP_(my_sv_catpvf_mg_nocontext)(SV * sv, const char * pat, ...);
+static
+#else
+extern void DPPP_(my_sv_catpvf_mg_nocontext)(SV * sv, const char * pat, ...);
+#endif
+
+#define sv_catpvf_mg_nocontext DPPP_(my_sv_catpvf_mg_nocontext)
+#define Perl_sv_catpvf_mg_nocontext DPPP_(my_sv_catpvf_mg_nocontext)
+
+#if defined(NEED_sv_catpvf_mg_nocontext) || defined(NEED_sv_catpvf_mg_nocontext_GLOBAL)
+
+void
+DPPP_(my_sv_catpvf_mg_nocontext)(SV *sv, const char *pat, ...)
+{
+ dTHX;
+ va_list args;
+ va_start(args, pat);
+ sv_vcatpvfn(sv, pat, strlen(pat), &args, Null(SV**), 0, Null(bool*));
+ SvSETMAGIC(sv);
+ va_end(args);
+}
+
+#endif
+#endif
+#endif
+
+/* sv_catpvf_mg depends on sv_catpvf_mg_nocontext */
+#ifndef sv_catpvf_mg
+# ifdef PERL_IMPLICIT_CONTEXT
+# define sv_catpvf_mg Perl_sv_catpvf_mg_nocontext
+# else
+# define sv_catpvf_mg Perl_sv_catpvf_mg
+# endif
+#endif
+
+#if (PERL_BCDVERSION >= 0x5004000) && !defined(sv_vcatpvf_mg)
+# define sv_vcatpvf_mg(sv, pat, args) \
+ STMT_START { \
+ sv_vcatpvfn(sv, pat, strlen(pat), args, Null(SV**), 0, Null(bool*)); \
+ SvSETMAGIC(sv); \
+ } STMT_END
+#endif
+
+#if (PERL_BCDVERSION >= 0x5004000) && !defined(sv_setpvf_mg)
+#if defined(NEED_sv_setpvf_mg)
+static void DPPP_(my_sv_setpvf_mg)(pTHX_ SV * sv, const char * pat, ...);
+static
+#else
+extern void DPPP_(my_sv_setpvf_mg)(pTHX_ SV * sv, const char * pat, ...);
+#endif
+
+#define Perl_sv_setpvf_mg DPPP_(my_sv_setpvf_mg)
+
+#if defined(NEED_sv_setpvf_mg) || defined(NEED_sv_setpvf_mg_GLOBAL)
+
+void
+DPPP_(my_sv_setpvf_mg)(pTHX_ SV *sv, const char *pat, ...)
+{
+ va_list args;
+ va_start(args, pat);
+ sv_vsetpvfn(sv, pat, strlen(pat), &args, Null(SV**), 0, Null(bool*));
+ SvSETMAGIC(sv);
+ va_end(args);
+}
+
+#endif
+#endif
+
+#ifdef PERL_IMPLICIT_CONTEXT
+#if (PERL_BCDVERSION >= 0x5004000) && !defined(sv_setpvf_mg_nocontext)
+#if defined(NEED_sv_setpvf_mg_nocontext)
+static void DPPP_(my_sv_setpvf_mg_nocontext)(SV * sv, const char * pat, ...);
+static
+#else
+extern void DPPP_(my_sv_setpvf_mg_nocontext)(SV * sv, const char * pat, ...);
+#endif
+
+#define sv_setpvf_mg_nocontext DPPP_(my_sv_setpvf_mg_nocontext)
+#define Perl_sv_setpvf_mg_nocontext DPPP_(my_sv_setpvf_mg_nocontext)
+
+#if defined(NEED_sv_setpvf_mg_nocontext) || defined(NEED_sv_setpvf_mg_nocontext_GLOBAL)
+
+void
+DPPP_(my_sv_setpvf_mg_nocontext)(SV *sv, const char *pat, ...)
+{
+ dTHX;
+ va_list args;
+ va_start(args, pat);
+ sv_vsetpvfn(sv, pat, strlen(pat), &args, Null(SV**), 0, Null(bool*));
+ SvSETMAGIC(sv);
+ va_end(args);
+}
+
+#endif
+#endif
+#endif
+
+/* sv_setpvf_mg depends on sv_setpvf_mg_nocontext */
+#ifndef sv_setpvf_mg
+# ifdef PERL_IMPLICIT_CONTEXT
+# define sv_setpvf_mg Perl_sv_setpvf_mg_nocontext
+# else
+# define sv_setpvf_mg Perl_sv_setpvf_mg
+# endif
+#endif
+
+#if (PERL_BCDVERSION >= 0x5004000) && !defined(sv_vsetpvf_mg)
+# define sv_vsetpvf_mg(sv, pat, args) \
+ STMT_START { \
+ sv_vsetpvfn(sv, pat, strlen(pat), args, Null(SV**), 0, Null(bool*)); \
+ SvSETMAGIC(sv); \
+ } STMT_END
+#endif
+
+#ifndef newSVpvn_share
+
+#if defined(NEED_newSVpvn_share)
+static SV * DPPP_(my_newSVpvn_share)(pTHX_ const char *src, I32 len, U32 hash);
+static
+#else
+extern SV * DPPP_(my_newSVpvn_share)(pTHX_ const char *src, I32 len, U32 hash);
+#endif
+
+#ifdef newSVpvn_share
+# undef newSVpvn_share
+#endif
+#define newSVpvn_share(a,b,c) DPPP_(my_newSVpvn_share)(aTHX_ a,b,c)
+#define Perl_newSVpvn_share DPPP_(my_newSVpvn_share)
+
+#if defined(NEED_newSVpvn_share) || defined(NEED_newSVpvn_share_GLOBAL)
+
+SV *
+DPPP_(my_newSVpvn_share)(pTHX_ const char *src, I32 len, U32 hash)
+{
+ SV *sv;
+ if (len < 0)
+ len = -len;
+ if (!hash)
+ PERL_HASH(hash, (char*) src, len);
+ sv = newSVpvn((char *) src, len);
+ sv_upgrade(sv, SVt_PVIV);
+ SvIVX(sv) = hash;
+ SvREADONLY_on(sv);
+ SvPOK_on(sv);
+ return sv;
+}
+
+#endif
+
+#endif
+#ifndef SvSHARED_HASH
+# define SvSHARED_HASH(sv) (0 + SvUVX(sv))
+#endif
+#ifndef WARN_ALL
+# define WARN_ALL 0
+#endif
+
+#ifndef WARN_CLOSURE
+# define WARN_CLOSURE 1
+#endif
+
+#ifndef WARN_DEPRECATED
+# define WARN_DEPRECATED 2
+#endif
+
+#ifndef WARN_EXITING
+# define WARN_EXITING 3
+#endif
+
+#ifndef WARN_GLOB
+# define WARN_GLOB 4
+#endif
+
+#ifndef WARN_IO
+# define WARN_IO 5
+#endif
+
+#ifndef WARN_CLOSED
+# define WARN_CLOSED 6
+#endif
+
+#ifndef WARN_EXEC
+# define WARN_EXEC 7
+#endif
+
+#ifndef WARN_LAYER
+# define WARN_LAYER 8
+#endif
+
+#ifndef WARN_NEWLINE
+# define WARN_NEWLINE 9
+#endif
+
+#ifndef WARN_PIPE
+# define WARN_PIPE 10
+#endif
+
+#ifndef WARN_UNOPENED
+# define WARN_UNOPENED 11
+#endif
+
+#ifndef WARN_MISC
+# define WARN_MISC 12
+#endif
+
+#ifndef WARN_NUMERIC
+# define WARN_NUMERIC 13
+#endif
+
+#ifndef WARN_ONCE
+# define WARN_ONCE 14
+#endif
+
+#ifndef WARN_OVERFLOW
+# define WARN_OVERFLOW 15
+#endif
+
+#ifndef WARN_PACK
+# define WARN_PACK 16
+#endif
+
+#ifndef WARN_PORTABLE
+# define WARN_PORTABLE 17
+#endif
+
+#ifndef WARN_RECURSION
+# define WARN_RECURSION 18
+#endif
+
+#ifndef WARN_REDEFINE
+# define WARN_REDEFINE 19
+#endif
+
+#ifndef WARN_REGEXP
+# define WARN_REGEXP 20
+#endif
+
+#ifndef WARN_SEVERE
+# define WARN_SEVERE 21
+#endif
+
+#ifndef WARN_DEBUGGING
+# define WARN_DEBUGGING 22
+#endif
+
+#ifndef WARN_INPLACE
+# define WARN_INPLACE 23
+#endif
+
+#ifndef WARN_INTERNAL
+# define WARN_INTERNAL 24
+#endif
+
+#ifndef WARN_MALLOC
+# define WARN_MALLOC 25
+#endif
+
+#ifndef WARN_SIGNAL
+# define WARN_SIGNAL 26
+#endif
+
+#ifndef WARN_SUBSTR
+# define WARN_SUBSTR 27
+#endif
+
+#ifndef WARN_SYNTAX
+# define WARN_SYNTAX 28
+#endif
+
+#ifndef WARN_AMBIGUOUS
+# define WARN_AMBIGUOUS 29
+#endif
+
+#ifndef WARN_BAREWORD
+# define WARN_BAREWORD 30
+#endif
+
+#ifndef WARN_DIGIT
+# define WARN_DIGIT 31
+#endif
+
+#ifndef WARN_PARENTHESIS
+# define WARN_PARENTHESIS 32
+#endif
+
+#ifndef WARN_PRECEDENCE
+# define WARN_PRECEDENCE 33
+#endif
+
+#ifndef WARN_PRINTF
+# define WARN_PRINTF 34
+#endif
+
+#ifndef WARN_PROTOTYPE
+# define WARN_PROTOTYPE 35
+#endif
+
+#ifndef WARN_QW
+# define WARN_QW 36
+#endif
+
+#ifndef WARN_RESERVED
+# define WARN_RESERVED 37
+#endif
+
+#ifndef WARN_SEMICOLON
+# define WARN_SEMICOLON 38
+#endif
+
+#ifndef WARN_TAINT
+# define WARN_TAINT 39
+#endif
+
+#ifndef WARN_THREADS
+# define WARN_THREADS 40
+#endif
+
+#ifndef WARN_UNINITIALIZED
+# define WARN_UNINITIALIZED 41
+#endif
+
+#ifndef WARN_UNPACK
+# define WARN_UNPACK 42
+#endif
+
+#ifndef WARN_UNTIE
+# define WARN_UNTIE 43
+#endif
+
+#ifndef WARN_UTF8
+# define WARN_UTF8 44
+#endif
+
+#ifndef WARN_VOID
+# define WARN_VOID 45
+#endif
+
+#ifndef WARN_ASSERTIONS
+# define WARN_ASSERTIONS 46
+#endif
+#ifndef packWARN
+# define packWARN(a) (a)
+#endif
+
+#ifndef ckWARN
+# ifdef G_WARN_ON
+# define ckWARN(a) (PL_dowarn & G_WARN_ON)
+# else
+# define ckWARN(a) PL_dowarn
+# endif
+#endif
+
+#if (PERL_BCDVERSION >= 0x5004000) && !defined(warner)
+#if defined(NEED_warner)
+static void DPPP_(my_warner)(U32 err, const char *pat, ...);
+static
+#else
+extern void DPPP_(my_warner)(U32 err, const char *pat, ...);
+#endif
+
+#define Perl_warner DPPP_(my_warner)
+
+#if defined(NEED_warner) || defined(NEED_warner_GLOBAL)
+
+void
+DPPP_(my_warner)(U32 err, const char *pat, ...)
+{
+ SV *sv;
+ va_list args;
+
+ PERL_UNUSED_ARG(err);
+
+ va_start(args, pat);
+ sv = vnewSVpvf(pat, &args);
+ va_end(args);
+ sv_2mortal(sv);
+ warn("%s", SvPV_nolen(sv));
+}
+
+#define warner Perl_warner
+
+#define Perl_warner_nocontext Perl_warner
+
+#endif
+#endif
+
+/* concatenating with "" ensures that only literal strings are accepted as argument
+ * note that STR_WITH_LEN() can't be used as argument to macros or functions that
+ * under some configurations might be macros
+ */
+#ifndef STR_WITH_LEN
+# define STR_WITH_LEN(s) (s ""), (sizeof(s)-1)
+#endif
+#ifndef newSVpvs
+# define newSVpvs(str) newSVpvn(str "", sizeof(str) - 1)
+#endif
+
+#ifndef sv_catpvs
+# define sv_catpvs(sv, str) sv_catpvn(sv, str "", sizeof(str) - 1)
+#endif
+
+#ifndef sv_setpvs
+# define sv_setpvs(sv, str) sv_setpvn(sv, str "", sizeof(str) - 1)
+#endif
+
+#ifndef hv_fetchs
+# define hv_fetchs(hv, key, lval) hv_fetch(hv, key "", sizeof(key) - 1, lval)
+#endif
+
+#ifndef hv_stores
+# define hv_stores(hv, key, val) hv_store(hv, key "", sizeof(key) - 1, val, 0)
+#endif
+#ifndef SvGETMAGIC
+# define SvGETMAGIC(x) STMT_START { if (SvGMAGICAL(x)) mg_get(x); } STMT_END
+#endif
+#ifndef PERL_MAGIC_sv
+# define PERL_MAGIC_sv '\0'
+#endif
+
+#ifndef PERL_MAGIC_overload
+# define PERL_MAGIC_overload 'A'
+#endif
+
+#ifndef PERL_MAGIC_overload_elem
+# define PERL_MAGIC_overload_elem 'a'
+#endif
+
+#ifndef PERL_MAGIC_overload_table
+# define PERL_MAGIC_overload_table 'c'
+#endif
+
+#ifndef PERL_MAGIC_bm
+# define PERL_MAGIC_bm 'B'
+#endif
+
+#ifndef PERL_MAGIC_regdata
+# define PERL_MAGIC_regdata 'D'
+#endif
+
+#ifndef PERL_MAGIC_regdatum
+# define PERL_MAGIC_regdatum 'd'
+#endif
+
+#ifndef PERL_MAGIC_env
+# define PERL_MAGIC_env 'E'
+#endif
+
+#ifndef PERL_MAGIC_envelem
+# define PERL_MAGIC_envelem 'e'
+#endif
+
+#ifndef PERL_MAGIC_fm
+# define PERL_MAGIC_fm 'f'
+#endif
+
+#ifndef PERL_MAGIC_regex_global
+# define PERL_MAGIC_regex_global 'g'
+#endif
+
+#ifndef PERL_MAGIC_isa
+# define PERL_MAGIC_isa 'I'
+#endif
+
+#ifndef PERL_MAGIC_isaelem
+# define PERL_MAGIC_isaelem 'i'
+#endif
+
+#ifndef PERL_MAGIC_nkeys
+# define PERL_MAGIC_nkeys 'k'
+#endif
+
+#ifndef PERL_MAGIC_dbfile
+# define PERL_MAGIC_dbfile 'L'
+#endif
+
+#ifndef PERL_MAGIC_dbline
+# define PERL_MAGIC_dbline 'l'
+#endif
+
+#ifndef PERL_MAGIC_mutex
+# define PERL_MAGIC_mutex 'm'
+#endif
+
+#ifndef PERL_MAGIC_shared
+# define PERL_MAGIC_shared 'N'
+#endif
+
+#ifndef PERL_MAGIC_shared_scalar
+# define PERL_MAGIC_shared_scalar 'n'
+#endif
+
+#ifndef PERL_MAGIC_collxfrm
+# define PERL_MAGIC_collxfrm 'o'
+#endif
+
+#ifndef PERL_MAGIC_tied
+# define PERL_MAGIC_tied 'P'
+#endif
+
+#ifndef PERL_MAGIC_tiedelem
+# define PERL_MAGIC_tiedelem 'p'
+#endif
+
+#ifndef PERL_MAGIC_tiedscalar
+# define PERL_MAGIC_tiedscalar 'q'
+#endif
+
+#ifndef PERL_MAGIC_qr
+# define PERL_MAGIC_qr 'r'
+#endif
+
+#ifndef PERL_MAGIC_sig
+# define PERL_MAGIC_sig 'S'
+#endif
+
+#ifndef PERL_MAGIC_sigelem
+# define PERL_MAGIC_sigelem 's'
+#endif
+
+#ifndef PERL_MAGIC_taint
+# define PERL_MAGIC_taint 't'
+#endif
+
+#ifndef PERL_MAGIC_uvar
+# define PERL_MAGIC_uvar 'U'
+#endif
+
+#ifndef PERL_MAGIC_uvar_elem
+# define PERL_MAGIC_uvar_elem 'u'
+#endif
+
+#ifndef PERL_MAGIC_vstring
+# define PERL_MAGIC_vstring 'V'
+#endif
+
+#ifndef PERL_MAGIC_vec
+# define PERL_MAGIC_vec 'v'
+#endif
+
+#ifndef PERL_MAGIC_utf8
+# define PERL_MAGIC_utf8 'w'
+#endif
+
+#ifndef PERL_MAGIC_substr
+# define PERL_MAGIC_substr 'x'
+#endif
+
+#ifndef PERL_MAGIC_defelem
+# define PERL_MAGIC_defelem 'y'
+#endif
+
+#ifndef PERL_MAGIC_glob
+# define PERL_MAGIC_glob '*'
+#endif
+
+#ifndef PERL_MAGIC_arylen
+# define PERL_MAGIC_arylen '#'
+#endif
+
+#ifndef PERL_MAGIC_pos
+# define PERL_MAGIC_pos '.'
+#endif
+
+#ifndef PERL_MAGIC_backref
+# define PERL_MAGIC_backref '<'
+#endif
+
+#ifndef PERL_MAGIC_ext
+# define PERL_MAGIC_ext '~'
+#endif
+
+/* That's the best we can do... */
+#ifndef sv_catpvn_nomg
+# define sv_catpvn_nomg sv_catpvn
+#endif
+
+#ifndef sv_catsv_nomg
+# define sv_catsv_nomg sv_catsv
+#endif
+
+#ifndef sv_setsv_nomg
+# define sv_setsv_nomg sv_setsv
+#endif
+
+#ifndef sv_pvn_nomg
+# define sv_pvn_nomg sv_pvn
+#endif
+
+#ifndef SvIV_nomg
+# define SvIV_nomg SvIV
+#endif
+
+#ifndef SvUV_nomg
+# define SvUV_nomg SvUV
+#endif
+
+#ifndef sv_catpv_mg
+# define sv_catpv_mg(sv, ptr) \
+ STMT_START { \
+ SV *TeMpSv = sv; \
+ sv_catpv(TeMpSv,ptr); \
+ SvSETMAGIC(TeMpSv); \
+ } STMT_END
+#endif
+
+#ifndef sv_catpvn_mg
+# define sv_catpvn_mg(sv, ptr, len) \
+ STMT_START { \
+ SV *TeMpSv = sv; \
+ sv_catpvn(TeMpSv,ptr,len); \
+ SvSETMAGIC(TeMpSv); \
+ } STMT_END
+#endif
+
+#ifndef sv_catsv_mg
+# define sv_catsv_mg(dsv, ssv) \
+ STMT_START { \
+ SV *TeMpSv = dsv; \
+ sv_catsv(TeMpSv,ssv); \
+ SvSETMAGIC(TeMpSv); \
+ } STMT_END
+#endif
+
+#ifndef sv_setiv_mg
+# define sv_setiv_mg(sv, i) \
+ STMT_START { \
+ SV *TeMpSv = sv; \
+ sv_setiv(TeMpSv,i); \
+ SvSETMAGIC(TeMpSv); \
+ } STMT_END
+#endif
+
+#ifndef sv_setnv_mg
+# define sv_setnv_mg(sv, num) \
+ STMT_START { \
+ SV *TeMpSv = sv; \
+ sv_setnv(TeMpSv,num); \
+ SvSETMAGIC(TeMpSv); \
+ } STMT_END
+#endif
+
+#ifndef sv_setpv_mg
+# define sv_setpv_mg(sv, ptr) \
+ STMT_START { \
+ SV *TeMpSv = sv; \
+ sv_setpv(TeMpSv,ptr); \
+ SvSETMAGIC(TeMpSv); \
+ } STMT_END
+#endif
+
+#ifndef sv_setpvn_mg
+# define sv_setpvn_mg(sv, ptr, len) \
+ STMT_START { \
+ SV *TeMpSv = sv; \
+ sv_setpvn(TeMpSv,ptr,len); \
+ SvSETMAGIC(TeMpSv); \
+ } STMT_END
+#endif
+
+#ifndef sv_setsv_mg
+# define sv_setsv_mg(dsv, ssv) \
+ STMT_START { \
+ SV *TeMpSv = dsv; \
+ sv_setsv(TeMpSv,ssv); \
+ SvSETMAGIC(TeMpSv); \
+ } STMT_END
+#endif
+
+#ifndef sv_setuv_mg
+# define sv_setuv_mg(sv, i) \
+ STMT_START { \
+ SV *TeMpSv = sv; \
+ sv_setuv(TeMpSv,i); \
+ SvSETMAGIC(TeMpSv); \
+ } STMT_END
+#endif
+
+#ifndef sv_usepvn_mg
+# define sv_usepvn_mg(sv, ptr, len) \
+ STMT_START { \
+ SV *TeMpSv = sv; \
+ sv_usepvn(TeMpSv,ptr,len); \
+ SvSETMAGIC(TeMpSv); \
+ } STMT_END
+#endif
+#ifndef SvVSTRING_mg
+# define SvVSTRING_mg(sv) (SvMAGICAL(sv) ? mg_find(sv, PERL_MAGIC_vstring) : NULL)
+#endif
+
+/* Hint: sv_magic_portable
+ * This is a compatibility function that is only available with
+ * Devel::PPPort. It is NOT in the perl core.
+ * Its purpose is to mimic the 5.8.0 behaviour of sv_magic() when
+ * it is being passed a name pointer with namlen == 0. In that
+ * case, perl 5.8.0 and later store the pointer, not a copy of it.
+ * The compatibility can be provided back to perl 5.004. With
+ * earlier versions, the code will not compile.
+ */
+
+#if (PERL_BCDVERSION < 0x5004000)
+
+ /* code that uses sv_magic_portable will not compile */
+
+#elif (PERL_BCDVERSION < 0x5008000)
+
+# define sv_magic_portable(sv, obj, how, name, namlen) \
+ STMT_START { \
+ SV *SvMp_sv = (sv); \
+ char *SvMp_name = (char *) (name); \
+ I32 SvMp_namlen = (namlen); \
+ if (SvMp_name && SvMp_namlen == 0) \
+ { \
+ MAGIC *mg; \
+ sv_magic(SvMp_sv, obj, how, 0, 0); \
+ mg = SvMAGIC(SvMp_sv); \
+ mg->mg_len = -42; /* XXX: this is the tricky part */ \
+ mg->mg_ptr = SvMp_name; \
+ } \
+ else \
+ { \
+ sv_magic(SvMp_sv, obj, how, SvMp_name, SvMp_namlen); \
+ } \
+ } STMT_END
+
+#else
+
+# define sv_magic_portable(a, b, c, d, e) sv_magic(a, b, c, d, e)
+
+#endif
+
+#ifdef USE_ITHREADS
+#ifndef CopFILE
+# define CopFILE(c) ((c)->cop_file)
+#endif
+
+#ifndef CopFILEGV
+# define CopFILEGV(c) (CopFILE(c) ? gv_fetchfile(CopFILE(c)) : Nullgv)
+#endif
+
+#ifndef CopFILE_set
+# define CopFILE_set(c,pv) ((c)->cop_file = savepv(pv))
+#endif
+
+#ifndef CopFILESV
+# define CopFILESV(c) (CopFILE(c) ? GvSV(gv_fetchfile(CopFILE(c))) : Nullsv)
+#endif
+
+#ifndef CopFILEAV
+# define CopFILEAV(c) (CopFILE(c) ? GvAV(gv_fetchfile(CopFILE(c))) : Nullav)
+#endif
+
+#ifndef CopSTASHPV
+# define CopSTASHPV(c) ((c)->cop_stashpv)
+#endif
+
+#ifndef CopSTASHPV_set
+# define CopSTASHPV_set(c,pv) ((c)->cop_stashpv = ((pv) ? savepv(pv) : Nullch))
+#endif
+
+#ifndef CopSTASH
+# define CopSTASH(c) (CopSTASHPV(c) ? gv_stashpv(CopSTASHPV(c),GV_ADD) : Nullhv)
+#endif
+
+#ifndef CopSTASH_set
+# define CopSTASH_set(c,hv) CopSTASHPV_set(c, (hv) ? HvNAME(hv) : Nullch)
+#endif
+
+#ifndef CopSTASH_eq
+# define CopSTASH_eq(c,hv) ((hv) && (CopSTASHPV(c) == HvNAME(hv) \
+ || (CopSTASHPV(c) && HvNAME(hv) \
+ && strEQ(CopSTASHPV(c), HvNAME(hv)))))
+#endif
+
+#else
+#ifndef CopFILEGV
+# define CopFILEGV(c) ((c)->cop_filegv)
+#endif
+
+#ifndef CopFILEGV_set
+# define CopFILEGV_set(c,gv) ((c)->cop_filegv = (GV*)SvREFCNT_inc(gv))
+#endif
+
+#ifndef CopFILE_set
+# define CopFILE_set(c,pv) CopFILEGV_set((c), gv_fetchfile(pv))
+#endif
+
+#ifndef CopFILESV
+# define CopFILESV(c) (CopFILEGV(c) ? GvSV(CopFILEGV(c)) : Nullsv)
+#endif
+
+#ifndef CopFILEAV
+# define CopFILEAV(c) (CopFILEGV(c) ? GvAV(CopFILEGV(c)) : Nullav)
+#endif
+
+#ifndef CopFILE
+# define CopFILE(c) (CopFILESV(c) ? SvPVX(CopFILESV(c)) : Nullch)
+#endif
+
+#ifndef CopSTASH
+# define CopSTASH(c) ((c)->cop_stash)
+#endif
+
+#ifndef CopSTASH_set
+# define CopSTASH_set(c,hv) ((c)->cop_stash = (hv))
+#endif
+
+#ifndef CopSTASHPV
+# define CopSTASHPV(c) (CopSTASH(c) ? HvNAME(CopSTASH(c)) : Nullch)
+#endif
+
+#ifndef CopSTASHPV_set
+# define CopSTASHPV_set(c,pv) CopSTASH_set((c), gv_stashpv(pv,GV_ADD))
+#endif
+
+#ifndef CopSTASH_eq
+# define CopSTASH_eq(c,hv) (CopSTASH(c) == (hv))
+#endif
+
+#endif /* USE_ITHREADS */
+#ifndef IN_PERL_COMPILETIME
+# define IN_PERL_COMPILETIME (PL_curcop == &PL_compiling)
+#endif
+
+#ifndef IN_LOCALE_RUNTIME
+# define IN_LOCALE_RUNTIME (PL_curcop->op_private & HINT_LOCALE)
+#endif
+
+#ifndef IN_LOCALE_COMPILETIME
+# define IN_LOCALE_COMPILETIME (PL_hints & HINT_LOCALE)
+#endif
+
+#ifndef IN_LOCALE
+# define IN_LOCALE (IN_PERL_COMPILETIME ? IN_LOCALE_COMPILETIME : IN_LOCALE_RUNTIME)
+#endif
+#ifndef IS_NUMBER_IN_UV
+# define IS_NUMBER_IN_UV 0x01
+#endif
+
+#ifndef IS_NUMBER_GREATER_THAN_UV_MAX
+# define IS_NUMBER_GREATER_THAN_UV_MAX 0x02
+#endif
+
+#ifndef IS_NUMBER_NOT_INT
+# define IS_NUMBER_NOT_INT 0x04
+#endif
+
+#ifndef IS_NUMBER_NEG
+# define IS_NUMBER_NEG 0x08
+#endif
+
+#ifndef IS_NUMBER_INFINITY
+# define IS_NUMBER_INFINITY 0x10
+#endif
+
+#ifndef IS_NUMBER_NAN
+# define IS_NUMBER_NAN 0x20
+#endif
+#ifndef GROK_NUMERIC_RADIX
+# define GROK_NUMERIC_RADIX(sp, send) grok_numeric_radix(sp, send)
+#endif
+#ifndef PERL_SCAN_GREATER_THAN_UV_MAX
+# define PERL_SCAN_GREATER_THAN_UV_MAX 0x02
+#endif
+
+#ifndef PERL_SCAN_SILENT_ILLDIGIT
+# define PERL_SCAN_SILENT_ILLDIGIT 0x04
+#endif
+
+#ifndef PERL_SCAN_ALLOW_UNDERSCORES
+# define PERL_SCAN_ALLOW_UNDERSCORES 0x01
+#endif
+
+#ifndef PERL_SCAN_DISALLOW_PREFIX
+# define PERL_SCAN_DISALLOW_PREFIX 0x02
+#endif
+
+#ifndef grok_numeric_radix
+#if defined(NEED_grok_numeric_radix)
+static bool DPPP_(my_grok_numeric_radix)(pTHX_ const char ** sp, const char * send);
+static
+#else
+extern bool DPPP_(my_grok_numeric_radix)(pTHX_ const char ** sp, const char * send);
+#endif
+
+#ifdef grok_numeric_radix
+# undef grok_numeric_radix
+#endif
+#define grok_numeric_radix(a,b) DPPP_(my_grok_numeric_radix)(aTHX_ a,b)
+#define Perl_grok_numeric_radix DPPP_(my_grok_numeric_radix)
+
+#if defined(NEED_grok_numeric_radix) || defined(NEED_grok_numeric_radix_GLOBAL)
+bool
+DPPP_(my_grok_numeric_radix)(pTHX_ const char **sp, const char *send)
+{
+#ifdef USE_LOCALE_NUMERIC
+#ifdef PL_numeric_radix_sv
+ if (PL_numeric_radix_sv && IN_LOCALE) {
+ STRLEN len;
+ char* radix = SvPV(PL_numeric_radix_sv, len);
+ if (*sp + len <= send && memEQ(*sp, radix, len)) {
+ *sp += len;
+ return TRUE;
+ }
+ }
+#else
+ /* older perls don't have PL_numeric_radix_sv so the radix
+ * must manually be requested from locale.h
+ */
+#include <locale.h>
+ dTHR; /* needed for older threaded perls */
+ struct lconv *lc = localeconv();
+ char *radix = lc->decimal_point;
+ if (radix && IN_LOCALE) {
+ STRLEN len = strlen(radix);
+ if (*sp + len <= send && memEQ(*sp, radix, len)) {
+ *sp += len;
+ return TRUE;
+ }
+ }
+#endif
+#endif /* USE_LOCALE_NUMERIC */
+ /* always try "." if numeric radix didn't match because
+ * we may have data from different locales mixed */
+ if (*sp < send && **sp == '.') {
+ ++*sp;
+ return TRUE;
+ }
+ return FALSE;
+}
+#endif
+#endif
+
+#ifndef grok_number
+#if defined(NEED_grok_number)
+static int DPPP_(my_grok_number)(pTHX_ const char * pv, STRLEN len, UV * valuep);
+static
+#else
+extern int DPPP_(my_grok_number)(pTHX_ const char * pv, STRLEN len, UV * valuep);
+#endif
+
+#ifdef grok_number
+# undef grok_number
+#endif
+#define grok_number(a,b,c) DPPP_(my_grok_number)(aTHX_ a,b,c)
+#define Perl_grok_number DPPP_(my_grok_number)
+
+#if defined(NEED_grok_number) || defined(NEED_grok_number_GLOBAL)
+int
+DPPP_(my_grok_number)(pTHX_ const char *pv, STRLEN len, UV *valuep)
+{
+ const char *s = pv;
+ const char *send = pv + len;
+ const UV max_div_10 = UV_MAX / 10;
+ const char max_mod_10 = UV_MAX % 10;
+ int numtype = 0;
+ int sawinf = 0;
+ int sawnan = 0;
+
+ while (s < send && isSPACE(*s))
+ s++;
+ if (s == send) {
+ return 0;
+ } else if (*s == '-') {
+ s++;
+ numtype = IS_NUMBER_NEG;
+ }
+ else if (*s == '+')
+ s++;
+
+ if (s == send)
+ return 0;
+
+ /* next must be digit or the radix separator or beginning of infinity */
+ if (isDIGIT(*s)) {
+ /* UVs are at least 32 bits, so the first 9 decimal digits cannot
+ overflow. */
+ UV value = *s - '0';
+ /* This construction seems to be more optimiser friendly.
+ (without it gcc does the isDIGIT test and the *s - '0' separately)
+ With it gcc on arm is managing 6 instructions (6 cycles) per digit.
+ In theory the optimiser could deduce how far to unroll the loop
+ before checking for overflow. */
+ if (++s < send) {
+ int digit = *s - '0';
+ if (digit >= 0 && digit <= 9) {
+ value = value * 10 + digit;
+ if (++s < send) {
+ digit = *s - '0';
+ if (digit >= 0 && digit <= 9) {
+ value = value * 10 + digit;
+ if (++s < send) {
+ digit = *s - '0';
+ if (digit >= 0 && digit <= 9) {
+ value = value * 10 + digit;
+ if (++s < send) {
+ digit = *s - '0';
+ if (digit >= 0 && digit <= 9) {
+ value = value * 10 + digit;
+ if (++s < send) {
+ digit = *s - '0';
+ if (digit >= 0 && digit <= 9) {
+ value = value * 10 + digit;
+ if (++s < send) {
+ digit = *s - '0';
+ if (digit >= 0 && digit <= 9) {
+ value = value * 10 + digit;
+ if (++s < send) {
+ digit = *s - '0';
+ if (digit >= 0 && digit <= 9) {
+ value = value * 10 + digit;
+ if (++s < send) {
+ digit = *s - '0';
+ if (digit >= 0 && digit <= 9) {
+ value = value * 10 + digit;
+ if (++s < send) {
+ /* Now got 9 digits, so need to check
+ each time for overflow. */
+ digit = *s - '0';
+ while (digit >= 0 && digit <= 9
+ && (value < max_div_10
+ || (value == max_div_10
+ && digit <= max_mod_10))) {
+ value = value * 10 + digit;
+ if (++s < send)
+ digit = *s - '0';
+ else
+ break;
+ }
+ if (digit >= 0 && digit <= 9
+ && (s < send)) {
+ /* value overflowed.
+ skip the remaining digits, don't
+ worry about setting *valuep. */
+ do {
+ s++;
+ } while (s < send && isDIGIT(*s));
+ numtype |=
+ IS_NUMBER_GREATER_THAN_UV_MAX;
+ goto skip_value;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ numtype |= IS_NUMBER_IN_UV;
+ if (valuep)
+ *valuep = value;
+
+ skip_value:
+ if (GROK_NUMERIC_RADIX(&s, send)) {
+ numtype |= IS_NUMBER_NOT_INT;
+ while (s < send && isDIGIT(*s)) /* optional digits after the radix */
+ s++;
+ }
+ }
+ else if (GROK_NUMERIC_RADIX(&s, send)) {
+ numtype |= IS_NUMBER_NOT_INT | IS_NUMBER_IN_UV; /* valuep assigned below */
+ /* no digits before the radix means we need digits after it */
+ if (s < send && isDIGIT(*s)) {
+ do {
+ s++;
+ } while (s < send && isDIGIT(*s));
+ if (valuep) {
+ /* integer approximation is valid - it's 0. */
+ *valuep = 0;
+ }
+ }
+ else
+ return 0;
+ } else if (*s == 'I' || *s == 'i') {
+ s++; if (s == send || (*s != 'N' && *s != 'n')) return 0;
+ s++; if (s == send || (*s != 'F' && *s != 'f')) return 0;
+ s++; if (s < send && (*s == 'I' || *s == 'i')) {
+ s++; if (s == send || (*s != 'N' && *s != 'n')) return 0;
+ s++; if (s == send || (*s != 'I' && *s != 'i')) return 0;
+ s++; if (s == send || (*s != 'T' && *s != 't')) return 0;
+ s++; if (s == send || (*s != 'Y' && *s != 'y')) return 0;
+ s++;
+ }
+ sawinf = 1;
+ } else if (*s == 'N' || *s == 'n') {
+ /* XXX TODO: There are signaling NaNs and quiet NaNs. */
+ s++; if (s == send || (*s != 'A' && *s != 'a')) return 0;
+ s++; if (s == send || (*s != 'N' && *s != 'n')) return 0;
+ s++;
+ sawnan = 1;
+ } else
+ return 0;
+
+ if (sawinf) {
+ numtype &= IS_NUMBER_NEG; /* Keep track of sign */
+ numtype |= IS_NUMBER_INFINITY | IS_NUMBER_NOT_INT;
+ } else if (sawnan) {
+ numtype &= IS_NUMBER_NEG; /* Keep track of sign */
+ numtype |= IS_NUMBER_NAN | IS_NUMBER_NOT_INT;
+ } else if (s < send) {
+ /* we can have an optional exponent part */
+ if (*s == 'e' || *s == 'E') {
+ /* The only flag we keep is sign. Blow away any "it's UV" */
+ numtype &= IS_NUMBER_NEG;
+ numtype |= IS_NUMBER_NOT_INT;
+ s++;
+ if (s < send && (*s == '-' || *s == '+'))
+ s++;
+ if (s < send && isDIGIT(*s)) {
+ do {
+ s++;
+ } while (s < send && isDIGIT(*s));
+ }
+ else
+ return 0;
+ }
+ }
+ while (s < send && isSPACE(*s))
+ s++;
+ if (s >= send)
+ return numtype;
+ if (len == 10 && memEQ(pv, "0 but true", 10)) {
+ if (valuep)
+ *valuep = 0;
+ return IS_NUMBER_IN_UV;
+ }
+ return 0;
+}
+#endif
+#endif
+
+/*
+ * The grok_* routines have been modified to use warn() instead of
+ * Perl_warner(). Also, 'hexdigit' was the former name of PL_hexdigit,
+ * which is why the stack variable has been renamed to 'xdigit'.
+ */
+
+#ifndef grok_bin
+#if defined(NEED_grok_bin)
+static UV DPPP_(my_grok_bin)(pTHX_ const char * start, STRLEN * len_p, I32 * flags, NV * result);
+static
+#else
+extern UV DPPP_(my_grok_bin)(pTHX_ const char * start, STRLEN * len_p, I32 * flags, NV * result);
+#endif
+
+#ifdef grok_bin
+# undef grok_bin
+#endif
+#define grok_bin(a,b,c,d) DPPP_(my_grok_bin)(aTHX_ a,b,c,d)
+#define Perl_grok_bin DPPP_(my_grok_bin)
+
+#if defined(NEED_grok_bin) || defined(NEED_grok_bin_GLOBAL)
+UV
+DPPP_(my_grok_bin)(pTHX_ const char *start, STRLEN *len_p, I32 *flags, NV *result)
+{
+ const char *s = start;
+ STRLEN len = *len_p;
+ UV value = 0;
+ NV value_nv = 0;
+
+ const UV max_div_2 = UV_MAX / 2;
+ bool allow_underscores = *flags & PERL_SCAN_ALLOW_UNDERSCORES;
+ bool overflowed = FALSE;
+
+ if (!(*flags & PERL_SCAN_DISALLOW_PREFIX)) {
+ /* strip off leading b or 0b.
+ for compatibility silently suffer "b" and "0b" as valid binary
+ numbers. */
+ if (len >= 1) {
+ if (s[0] == 'b') {
+ s++;
+ len--;
+ }
+ else if (len >= 2 && s[0] == '0' && s[1] == 'b') {
+ s+=2;
+ len-=2;
+ }
+ }
+ }
+
+ for (; len-- && *s; s++) {
+ char bit = *s;
+ if (bit == '0' || bit == '1') {
+ /* Write it in this wonky order with a goto to attempt to get the
+ compiler to make the common case integer-only loop pretty tight.
+ With gcc seems to be much straighter code than old scan_bin. */
+ redo:
+ if (!overflowed) {
+ if (value <= max_div_2) {
+ value = (value << 1) | (bit - '0');
+ continue;
+ }
+ /* Bah. We're just overflowed. */
+ warn("Integer overflow in binary number");
+ overflowed = TRUE;
+ value_nv = (NV) value;
+ }
+ value_nv *= 2.0;
+ /* If an NV has not enough bits in its mantissa to
+ * represent a UV this summing of small low-order numbers
+ * is a waste of time (because the NV cannot preserve
+ * the low-order bits anyway): we could just remember when
+ * did we overflow and in the end just multiply value_nv by the
+ * right amount. */
+ value_nv += (NV)(bit - '0');
+ continue;
+ }
+ if (bit == '_' && len && allow_underscores && (bit = s[1])
+ && (bit == '0' || bit == '1'))
+ {
+ --len;
+ ++s;
+ goto redo;
+ }
+ if (!(*flags & PERL_SCAN_SILENT_ILLDIGIT))
+ warn("Illegal binary digit '%c' ignored", *s);
+ break;
+ }
+
+ if ( ( overflowed && value_nv > 4294967295.0)
+#if UVSIZE > 4
+ || (!overflowed && value > 0xffffffff )
+#endif
+ ) {
+ warn("Binary number > 0b11111111111111111111111111111111 non-portable");
+ }
+ *len_p = s - start;
+ if (!overflowed) {
+ *flags = 0;
+ return value;
+ }
+ *flags = PERL_SCAN_GREATER_THAN_UV_MAX;
+ if (result)
+ *result = value_nv;
+ return UV_MAX;
+}
+#endif
+#endif
+
+#ifndef grok_hex
+#if defined(NEED_grok_hex)
+static UV DPPP_(my_grok_hex)(pTHX_ const char * start, STRLEN * len_p, I32 * flags, NV * result);
+static
+#else
+extern UV DPPP_(my_grok_hex)(pTHX_ const char * start, STRLEN * len_p, I32 * flags, NV * result);
+#endif
+
+#ifdef grok_hex
+# undef grok_hex
+#endif
+#define grok_hex(a,b,c,d) DPPP_(my_grok_hex)(aTHX_ a,b,c,d)
+#define Perl_grok_hex DPPP_(my_grok_hex)
+
+#if defined(NEED_grok_hex) || defined(NEED_grok_hex_GLOBAL)
+UV
+DPPP_(my_grok_hex)(pTHX_ const char *start, STRLEN *len_p, I32 *flags, NV *result)
+{
+ const char *s = start;
+ STRLEN len = *len_p;
+ UV value = 0;
+ NV value_nv = 0;
+
+ const UV max_div_16 = UV_MAX / 16;
+ bool allow_underscores = *flags & PERL_SCAN_ALLOW_UNDERSCORES;
+ bool overflowed = FALSE;
+ const char *xdigit;
+
+ if (!(*flags & PERL_SCAN_DISALLOW_PREFIX)) {
+ /* strip off leading x or 0x.
+ for compatibility silently suffer "x" and "0x" as valid hex numbers.
+ */
+ if (len >= 1) {
+ if (s[0] == 'x') {
+ s++;
+ len--;
+ }
+ else if (len >= 2 && s[0] == '0' && s[1] == 'x') {
+ s+=2;
+ len-=2;
+ }
+ }
+ }
+
+ for (; len-- && *s; s++) {
+ xdigit = strchr((char *) PL_hexdigit, *s);
+ if (xdigit) {
+ /* Write it in this wonky order with a goto to attempt to get the
+ compiler to make the common case integer-only loop pretty tight.
+ With gcc seems to be much straighter code than old scan_hex. */
+ redo:
+ if (!overflowed) {
+ if (value <= max_div_16) {
+ value = (value << 4) | ((xdigit - PL_hexdigit) & 15);
+ continue;
+ }
+ warn("Integer overflow in hexadecimal number");
+ overflowed = TRUE;
+ value_nv = (NV) value;
+ }
+ value_nv *= 16.0;
+ /* If an NV has not enough bits in its mantissa to
+ * represent a UV this summing of small low-order numbers
+ * is a waste of time (because the NV cannot preserve
+ * the low-order bits anyway): we could just remember when
+ * did we overflow and in the end just multiply value_nv by the
+ * right amount of 16-tuples. */
+ value_nv += (NV)((xdigit - PL_hexdigit) & 15);
+ continue;
+ }
+ if (*s == '_' && len && allow_underscores && s[1]
+ && (xdigit = strchr((char *) PL_hexdigit, s[1])))
+ {
+ --len;
+ ++s;
+ goto redo;
+ }
+ if (!(*flags & PERL_SCAN_SILENT_ILLDIGIT))
+ warn("Illegal hexadecimal digit '%c' ignored", *s);
+ break;
+ }
+
+ if ( ( overflowed && value_nv > 4294967295.0)
+#if UVSIZE > 4
+ || (!overflowed && value > 0xffffffff )
+#endif
+ ) {
+ warn("Hexadecimal number > 0xffffffff non-portable");
+ }
+ *len_p = s - start;
+ if (!overflowed) {
+ *flags = 0;
+ return value;
+ }
+ *flags = PERL_SCAN_GREATER_THAN_UV_MAX;
+ if (result)
+ *result = value_nv;
+ return UV_MAX;
+}
+#endif
+#endif
+
+#ifndef grok_oct
+#if defined(NEED_grok_oct)
+static UV DPPP_(my_grok_oct)(pTHX_ const char * start, STRLEN * len_p, I32 * flags, NV * result);
+static
+#else
+extern UV DPPP_(my_grok_oct)(pTHX_ const char * start, STRLEN * len_p, I32 * flags, NV * result);
+#endif
+
+#ifdef grok_oct
+# undef grok_oct
+#endif
+#define grok_oct(a,b,c,d) DPPP_(my_grok_oct)(aTHX_ a,b,c,d)
+#define Perl_grok_oct DPPP_(my_grok_oct)
+
+#if defined(NEED_grok_oct) || defined(NEED_grok_oct_GLOBAL)
+UV
+DPPP_(my_grok_oct)(pTHX_ const char *start, STRLEN *len_p, I32 *flags, NV *result)
+{
+ const char *s = start;
+ STRLEN len = *len_p;
+ UV value = 0;
+ NV value_nv = 0;
+
+ const UV max_div_8 = UV_MAX / 8;
+ bool allow_underscores = *flags & PERL_SCAN_ALLOW_UNDERSCORES;
+ bool overflowed = FALSE;
+
+ for (; len-- && *s; s++) {
+ /* gcc 2.95 optimiser not smart enough to figure that this subtraction
+ out front allows slicker code. */
+ int digit = *s - '0';
+ if (digit >= 0 && digit <= 7) {
+ /* Write it in this wonky order with a goto to attempt to get the
+ compiler to make the common case integer-only loop pretty tight.
+ */
+ redo:
+ if (!overflowed) {
+ if (value <= max_div_8) {
+ value = (value << 3) | digit;
+ continue;
+ }
+ /* Bah. We're just overflowed. */
+ warn("Integer overflow in octal number");
+ overflowed = TRUE;
+ value_nv = (NV) value;
+ }
+ value_nv *= 8.0;
+ /* If an NV has not enough bits in its mantissa to
+ * represent a UV this summing of small low-order numbers
+ * is a waste of time (because the NV cannot preserve
+ * the low-order bits anyway): we could just remember when
+ * did we overflow and in the end just multiply value_nv by the
+ * right amount of 8-tuples. */
+ value_nv += (NV)digit;
+ continue;
+ }
+ if (digit == ('_' - '0') && len && allow_underscores
+ && (digit = s[1] - '0') && (digit >= 0 && digit <= 7))
+ {
+ --len;
+ ++s;
+ goto redo;
+ }
+ /* Allow \octal to work the DWIM way (that is, stop scanning
+ * as soon as non-octal characters are seen, complain only iff
+ * someone seems to want to use the digits eight and nine). */
+ if (digit == 8 || digit == 9) {
+ if (!(*flags & PERL_SCAN_SILENT_ILLDIGIT))
+ warn("Illegal octal digit '%c' ignored", *s);
+ }
+ break;
+ }
+
+ if ( ( overflowed && value_nv > 4294967295.0)
+#if UVSIZE > 4
+ || (!overflowed && value > 0xffffffff )
+#endif
+ ) {
+ warn("Octal number > 037777777777 non-portable");
+ }
+ *len_p = s - start;
+ if (!overflowed) {
+ *flags = 0;
+ return value;
+ }
+ *flags = PERL_SCAN_GREATER_THAN_UV_MAX;
+ if (result)
+ *result = value_nv;
+ return UV_MAX;
+}
+#endif
+#endif
+
+#if !defined(my_snprintf)
+#if defined(NEED_my_snprintf)
+static int DPPP_(my_my_snprintf)(char * buffer, const Size_t len, const char * format, ...);
+static
+#else
+extern int DPPP_(my_my_snprintf)(char * buffer, const Size_t len, const char * format, ...);
+#endif
+
+#define my_snprintf DPPP_(my_my_snprintf)
+#define Perl_my_snprintf DPPP_(my_my_snprintf)
+
+#if defined(NEED_my_snprintf) || defined(NEED_my_snprintf_GLOBAL)
+
+int
+DPPP_(my_my_snprintf)(char *buffer, const Size_t len, const char *format, ...)
+{
+ dTHX;
+ int retval;
+ va_list ap;
+ va_start(ap, format);
+#ifdef HAS_VSNPRINTF
+ retval = vsnprintf(buffer, len, format, ap);
+#else
+ retval = vsprintf(buffer, format, ap);
+#endif
+ va_end(ap);
+ if (retval >= (int)len)
+ Perl_croak(aTHX_ "panic: my_snprintf buffer overflow");
+ return retval;
+}
+
+#endif
+#endif
+
+#ifdef NO_XSLOCKS
+# ifdef dJMPENV
+# define dXCPT dJMPENV; int rEtV = 0
+# define XCPT_TRY_START JMPENV_PUSH(rEtV); if (rEtV == 0)
+# define XCPT_TRY_END JMPENV_POP;
+# define XCPT_CATCH if (rEtV != 0)
+# define XCPT_RETHROW JMPENV_JUMP(rEtV)
+# else
+# define dXCPT Sigjmp_buf oldTOP; int rEtV = 0
+# define XCPT_TRY_START Copy(top_env, oldTOP, 1, Sigjmp_buf); rEtV = Sigsetjmp(top_env, 1); if (rEtV == 0)
+# define XCPT_TRY_END Copy(oldTOP, top_env, 1, Sigjmp_buf);
+# define XCPT_CATCH if (rEtV != 0)
+# define XCPT_RETHROW Siglongjmp(top_env, rEtV)
+# endif
+#endif
+
+#if !defined(my_strlcat)
+#if defined(NEED_my_strlcat)
+static Size_t DPPP_(my_my_strlcat)(char * dst, const char * src, Size_t size);
+static
+#else
+extern Size_t DPPP_(my_my_strlcat)(char * dst, const char * src, Size_t size);
+#endif
+
+#define my_strlcat DPPP_(my_my_strlcat)
+#define Perl_my_strlcat DPPP_(my_my_strlcat)
+
+#if defined(NEED_my_strlcat) || defined(NEED_my_strlcat_GLOBAL)
+
+Size_t
+DPPP_(my_my_strlcat)(char *dst, const char *src, Size_t size)
+{
+ Size_t used, length, copy;
+
+ used = strlen(dst);
+ length = strlen(src);
+ if (size > 0 && used < size - 1) {
+ copy = (length >= size - used) ? size - used - 1 : length;
+ memcpy(dst + used, src, copy);
+ dst[used + copy] = '\0';
+ }
+ return used + length;
+}
+#endif
+#endif
+
+#if !defined(my_strlcpy)
+#if defined(NEED_my_strlcpy)
+static Size_t DPPP_(my_my_strlcpy)(char * dst, const char * src, Size_t size);
+static
+#else
+extern Size_t DPPP_(my_my_strlcpy)(char * dst, const char * src, Size_t size);
+#endif
+
+#define my_strlcpy DPPP_(my_my_strlcpy)
+#define Perl_my_strlcpy DPPP_(my_my_strlcpy)
+
+#if defined(NEED_my_strlcpy) || defined(NEED_my_strlcpy_GLOBAL)
+
+Size_t
+DPPP_(my_my_strlcpy)(char *dst, const char *src, Size_t size)
+{
+ Size_t length, copy;
+
+ length = strlen(src);
+ if (size > 0) {
+ copy = (length >= size) ? size - 1 : length;
+ memcpy(dst, src, copy);
+ dst[copy] = '\0';
+ }
+ return length;
+}
+
+#endif
+#endif
+
+#endif /* _P_P_PORTABILITY_H_ */
+
+/* End of File ppport.h */
diff --git a/plugin/handler_socket/perl-Net-HandlerSocket/t/HandlerSocket.t b/plugin/handler_socket/perl-Net-HandlerSocket/t/HandlerSocket.t
new file mode 100644
index 00000000000..adb7c981e23
--- /dev/null
+++ b/plugin/handler_socket/perl-Net-HandlerSocket/t/HandlerSocket.t
@@ -0,0 +1,15 @@
+# Before `make install' is performed this script should be runnable with
+# `make test'. After `make install' it should work as `perl HandlerSocket.t'
+
+#########################
+
+# change 'tests => 1' to 'tests => last_test_to_print';
+
+use Test::More tests => 1;
+BEGIN { use_ok('Net::HandlerSocket') };
+
+#########################
+
+# Insert your test code below, the Test::More module is use()ed here so read
+# its man page ( perldoc Test::More ) for help writing this test script.
+
diff --git a/plugin/handler_socket/plug.in b/plugin/handler_socket/plug.in
new file mode 100644
index 00000000000..0a6c36ba39f
--- /dev/null
+++ b/plugin/handler_socket/plug.in
@@ -0,0 +1,19 @@
+MYSQL_PLUGIN(handlersocket, [HandlerSocket], [HandlerSocket], [max])
+MYSQL_PLUGIN_DYNAMIC(handlersocket, handlersocket.la)
+MYSQL_PLUGIN_ACTIONS(handlersocket,
+[
+ ac_mysql_source_dir='$(top_srcdir)'
+ MYSQL_INC="-I$ac_mysql_source_dir/sql"
+ MYSQL_INC="$MYSQL_INC -I$ac_mysql_source_dir/include"
+ MYSQL_INC="$MYSQL_INC -I$ac_mysql_source_dir/regex"
+ MYSQL_INC="$MYSQL_INC -I$ac_mysql_source_dir"
+ MYSQL_LIB='-L$(top_builddir)/libservices -lmysqlservices'
+ PLUGIN_DIR='$(pkglibdir)/plugin'
+ HANDLERSOCKET_SUBDIRS="libhsclient handlersocket client"
+
+ AC_SUBST(MYSQL_INC)
+ AC_SUBST(MYSQL_CFLAGS)
+ AC_SUBST(MYSQL_LIB)
+ AC_SUBST(PLUGIN_DIR)
+ AC_SUBST(HANDLERSOCKET_SUBDIRS)
+])
diff --git a/plugin/handler_socket/regtest/common/binary_my.cnf b/plugin/handler_socket/regtest/common/binary_my.cnf
new file mode 100644
index 00000000000..c3f7c02c7c6
--- /dev/null
+++ b/plugin/handler_socket/regtest/common/binary_my.cnf
@@ -0,0 +1,4 @@
+
+[perl]
+default-character-set-name = binary
+
diff --git a/plugin/handler_socket/regtest/common/compat.sh b/plugin/handler_socket/regtest/common/compat.sh
new file mode 100644
index 00000000000..7804bdf16e1
--- /dev/null
+++ b/plugin/handler_socket/regtest/common/compat.sh
@@ -0,0 +1,29 @@
+#!/bin/sh
+
+if [ "`uname -o`" = "Cygwin" ]; then
+ export DIFF='diff --ignore-space --strip-trailing-cr'
+elif [ "`uname`" = "Darwin" ]; then
+ export DIFF='diff'
+else
+ export DIFF='diff --ignore-space --strip-trailing-cr'
+fi
+
+compile_c() {
+ if [ "`uname -o`" = "Cygwin" ]; then
+ cl /W3 /I../.. /EHsc /FD /MD "$1" /link /DLL "/OUT:$2.dll" \
+ ../../libase.lib 2> cl.log
+ else
+ $CXX -I../.. -O3 -g -Wall -fPIC -shared "$1" -o "$2.so"
+ fi
+}
+
+compile_j() {
+ if [ "`uname -o`" = "Cygwin" ]; then
+ jdk="`echo /cygdrive/c/Program\ Files/Java/jdk* | head -1`"
+ else
+ jdk="$SUNJDK"
+ fi
+ "$jdk/bin/javac" -g "$1"/*.java
+ "$jdk/bin/jar" -cf "$1.jar" "$1"/*.class
+}
+
diff --git a/plugin/handler_socket/regtest/common/hstest.pm b/plugin/handler_socket/regtest/common/hstest.pm
new file mode 100644
index 00000000000..35cc75aad2f
--- /dev/null
+++ b/plugin/handler_socket/regtest/common/hstest.pm
@@ -0,0 +1,62 @@
+
+# vim:sw=2:ai
+
+package hstest;
+
+use DBI;
+use Net::HandlerSocket;
+
+our %conf = ();
+
+sub get_conf_env {
+ my ($key, $defval) = @_;
+ return $ENV{$key} || $defval;
+}
+
+sub init_conf {
+ $conf{host} = get_conf_env("MYHOST", "localhost");
+ $conf{myport} = get_conf_env("MYPORT", 3306);
+ $conf{dbname} = get_conf_env("MYDBNAME", "hstestdb");
+ $conf{ssps} = get_conf_env("MYSSPS");
+ $conf{user} = get_conf_env("MYSQLUSER", "root");
+ $conf{pass} = get_conf_env("MYSQLPASS", "");
+ $conf{hsport} = get_conf_env("HSPORT", 9998);
+}
+
+sub get_dbi_connection {
+ my ($dbname, $host, $myport, $ssps, $user, $pass)
+ = ($conf{dbname}, $conf{host}, $conf{myport}, $conf{ssps},
+ $conf{user}, $conf{pass});
+ my $mycnf = "binary_my.cnf";
+ my $dsn = "DBI:mysql:database=;host=$host;port=$myport"
+ . ";mysql_server_prepare=$ssps"
+ . ";mysql_read_default_group=perl"
+ . ";mysql_read_default_file=../common/$mycnf";
+ my $dbh = DBI->connect($dsn, $user, $pass, { RaiseError => 1 });
+ return $dbh;
+}
+
+sub init_testdb {
+ my $charset = $_[0] || "binary";
+ my $dbh = get_dbi_connection();
+ my $dbname = $conf{dbname};
+ $dbh->do("drop database if exists $dbname");
+ $dbh->do("create database $dbname default character set $charset");
+ $dbh->do("use $dbname");
+ return $dbh;
+}
+
+sub get_hs_connection {
+ my ($host, $port) = @_;
+ $host ||= $conf{host};
+ $port ||= $conf{hsport};
+ my $hsargs = { 'host' => $host, 'port' => $port };
+ my $conn = new Net::HandlerSocket($hsargs);
+ return $conn;
+}
+
+
+init_conf();
+
+1;
+
diff --git a/plugin/handler_socket/regtest/test_01_lib/run.sh b/plugin/handler_socket/regtest/test_01_lib/run.sh
new file mode 100755
index 00000000000..07b6c37b80c
--- /dev/null
+++ b/plugin/handler_socket/regtest/test_01_lib/run.sh
@@ -0,0 +1,27 @@
+#!/bin/bash
+
+TESTS="01 02 03 04 05 06 07 08 09 10 11 12 13";
+
+source ../common/compat.sh
+
+for i in $TESTS; do
+ perl "test$i.pl" > test$i.log 2> test$i.log2
+done
+for i in $TESTS; do
+ if ! $DIFF -u test$i.log test$i.expected; then
+ echo "test$i failed";
+ exit 1
+ fi
+ if [ -f "test$i.expect2" ]; then
+ lines="`wc -l < test$i.expect2`"
+ head -$lines test$i.log2 > test$i.log2h
+ if ! $DIFF -u test$i.log2h test$i.expect2 && \
+ ! $DIFF -u test$i.log2h test$i.expect2ef; then
+ echo "test$i failed";
+ exit 1
+ fi
+ fi
+done
+echo "OK."
+exit 0
+
diff --git a/plugin/handler_socket/regtest/test_01_lib/test01.expected b/plugin/handler_socket/regtest/test_01_lib/test01.expected
new file mode 100644
index 00000000000..37da3242f23
--- /dev/null
+++ b/plugin/handler_socket/regtest/test_01_lib/test01.expected
@@ -0,0 +1,100 @@
+k0 v1020
+k1 v6351
+k10 v70410
+k11 v75111
+k12 v36712
+k13 v40013
+k14 v39714
+k15 v17015
+k16 v71916
+k17 v73417
+k18 v58718
+k19 v49419
+k2 v8032
+k20 v52320
+k21 v95421
+k22 v43322
+k23 v82023
+k24 v28324
+k25 v83725
+k26 v20526
+k27 v41527
+k28 v54528
+k29 v58329
+k3 v9253
+k30 v5230
+k31 v32331
+k32 v61432
+k33 v67933
+k34 v80534
+k35 v45135
+k36 v11536
+k37 v26937
+k38 v21838
+k39 v61739
+k4 v7754
+k40 v87840
+k41 v34541
+k42 v51242
+k43 v96943
+k44 v40844
+k45 v29145
+k46 v85846
+k47 v95347
+k48 v71048
+k49 v14249
+k5 v5375
+k50 v68250
+k51 v93451
+k52 v62152
+k53 v96553
+k54 v57454
+k55 v20455
+k56 v29856
+k57 v13457
+k58 v98358
+k59 v44459
+k6 v5926
+k60 v14460
+k61 v15261
+k62 v18762
+k63 v21563
+k64 v864
+k65 v69765
+k66 v65166
+k67 v28067
+k68 v70168
+k69 v53769
+k7 v4147
+k70 v41370
+k71 v6971
+k72 v8672
+k73 v82273
+k74 v67074
+k75 v37075
+k76 v80676
+k77 v68877
+k78 v2678
+k79 v6679
+k8 v5908
+k80 v80280
+k81 v17181
+k82 v55782
+k83 v84783
+k84 v77784
+k85 v73085
+k86 v98786
+k87 v11587
+k88 v64688
+k89 v49689
+k9 v3029
+k90 v12090
+k91 v68491
+k92 v37492
+k93 v6593
+k94 v37094
+k95 v17495
+k96 v82896
+k97 v86797
+k98 v75998
+k99 v70399
diff --git a/plugin/handler_socket/regtest/test_01_lib/test01.pl b/plugin/handler_socket/regtest/test_01_lib/test01.pl
new file mode 100644
index 00000000000..d3a072fb3cc
--- /dev/null
+++ b/plugin/handler_socket/regtest/test_01_lib/test01.pl
@@ -0,0 +1,38 @@
+#!/usr/bin/perl
+
+# vim:sw=2:ai
+
+# test for libmysql
+
+BEGIN {
+ push @INC, "../common/";
+};
+
+use strict;
+use warnings;
+use hstest;
+
+my $dbh = hstest::init_testdb();
+my $table = 'hstesttbl';
+my $tablesize = 100;
+$dbh->do(
+ "create table $table (k varchar(30) primary key, v varchar(30) not null) " .
+ "engine = innodb");
+srand(999);
+
+my %valmap = ();
+
+my $sth = $dbh->prepare("insert into $table values (?,?)");
+for (my $i = 0; $i < $tablesize; ++$i) {
+ my $k = "k" . $i;
+ my $v = "v" . int(rand(1000)) . $i;
+ $sth->execute($k, $v);
+ $valmap{$k} = $v;
+}
+
+my $aref = $dbh->selectall_arrayref("select k,v from $table order by k");
+for my $row (@$aref) {
+ my ($k, $v) = @$row;
+ print "$k $v\n";
+}
+
diff --git a/plugin/handler_socket/regtest/test_01_lib/test02.expected b/plugin/handler_socket/regtest/test_01_lib/test02.expected
new file mode 100644
index 00000000000..37da3242f23
--- /dev/null
+++ b/plugin/handler_socket/regtest/test_01_lib/test02.expected
@@ -0,0 +1,100 @@
+k0 v1020
+k1 v6351
+k10 v70410
+k11 v75111
+k12 v36712
+k13 v40013
+k14 v39714
+k15 v17015
+k16 v71916
+k17 v73417
+k18 v58718
+k19 v49419
+k2 v8032
+k20 v52320
+k21 v95421
+k22 v43322
+k23 v82023
+k24 v28324
+k25 v83725
+k26 v20526
+k27 v41527
+k28 v54528
+k29 v58329
+k3 v9253
+k30 v5230
+k31 v32331
+k32 v61432
+k33 v67933
+k34 v80534
+k35 v45135
+k36 v11536
+k37 v26937
+k38 v21838
+k39 v61739
+k4 v7754
+k40 v87840
+k41 v34541
+k42 v51242
+k43 v96943
+k44 v40844
+k45 v29145
+k46 v85846
+k47 v95347
+k48 v71048
+k49 v14249
+k5 v5375
+k50 v68250
+k51 v93451
+k52 v62152
+k53 v96553
+k54 v57454
+k55 v20455
+k56 v29856
+k57 v13457
+k58 v98358
+k59 v44459
+k6 v5926
+k60 v14460
+k61 v15261
+k62 v18762
+k63 v21563
+k64 v864
+k65 v69765
+k66 v65166
+k67 v28067
+k68 v70168
+k69 v53769
+k7 v4147
+k70 v41370
+k71 v6971
+k72 v8672
+k73 v82273
+k74 v67074
+k75 v37075
+k76 v80676
+k77 v68877
+k78 v2678
+k79 v6679
+k8 v5908
+k80 v80280
+k81 v17181
+k82 v55782
+k83 v84783
+k84 v77784
+k85 v73085
+k86 v98786
+k87 v11587
+k88 v64688
+k89 v49689
+k9 v3029
+k90 v12090
+k91 v68491
+k92 v37492
+k93 v6593
+k94 v37094
+k95 v17495
+k96 v82896
+k97 v86797
+k98 v75998
+k99 v70399
diff --git a/plugin/handler_socket/regtest/test_01_lib/test02.pl b/plugin/handler_socket/regtest/test_01_lib/test02.pl
new file mode 100644
index 00000000000..c69515d76e9
--- /dev/null
+++ b/plugin/handler_socket/regtest/test_01_lib/test02.pl
@@ -0,0 +1,49 @@
+#!/usr/bin/perl
+
+# vim:sw=2:ai
+
+# test for '>='
+
+BEGIN {
+ push @INC, "../common/";
+};
+
+use strict;
+use warnings;
+use hstest;
+
+my $dbh = hstest::init_testdb();
+my $table = 'hstesttbl';
+my $tablesize = 100;
+$dbh->do(
+ "create table $table (k varchar(30) primary key, v varchar(30) not null) " .
+ "engine = innodb");
+srand(999);
+
+my %valmap = ();
+
+my $sth = $dbh->prepare("insert into $table values (?,?)");
+for (my $i = 0; $i < $tablesize; ++$i) {
+ my $k = "k" . $i;
+ my $v = "v" . int(rand(1000)) . $i;
+ $sth->execute($k, $v);
+ $valmap{$k} = $v;
+}
+
+my $hs = hstest::get_hs_connection();
+my $dbname = $hstest::conf{dbname};
+$hs->open_index(1, $dbname, $table, '', 'k,v');
+my $r = $hs->execute_single(1, '>=', [ '' ], 10000, 0);
+shift(@$r);
+for (my $i = 0; $i < $tablesize; ++$i) {
+ my $k = $r->[$i * 2];
+ my $v = $r->[$i * 2 + 1];
+ print "$k $v\n";
+}
+
+my $aref = $dbh->selectall_arrayref("select k,v from $table order by k");
+for my $row (@$aref) {
+ my ($k, $v) = @$row;
+ #print "$k $v\n";
+}
+
diff --git a/plugin/handler_socket/regtest/test_01_lib/test03.expected b/plugin/handler_socket/regtest/test_01_lib/test03.expected
new file mode 100644
index 00000000000..87b90cd0b86
--- /dev/null
+++ b/plugin/handler_socket/regtest/test_01_lib/test03.expected
@@ -0,0 +1,771 @@
+WR
+0 0
+1 1
+2 2
+3 3
+4 4
+5 5
+6 6
+7 7
+8 8
+9 9
+10 10
+11 11
+12 12
+13 13
+14 14
+15 15
+16 16
+17 17
+18 18
+19 19
+20 20
+21 21
+22 22
+23 23
+24 24
+25 25
+26 26
+27 27
+28 28
+29 29
+30 30
+31 31
+32 32
+33 33
+34 34
+35 35
+36 36
+37 37
+38 38
+39 39
+40 40
+41 41
+42 42
+43 43
+44 44
+45 45
+46 46
+47 47
+48 48
+49 49
+50 50
+51 51
+52 52
+53 53
+54 54
+55 55
+56 56
+57 57
+58 58
+59 59
+60 60
+61 61
+62 62
+63 63
+64 64
+65 65
+66 66
+67 67
+68 68
+69 69
+70 70
+71 71
+72 72
+73 73
+74 74
+75 75
+76 76
+77 77
+78 78
+79 79
+80 80
+81 81
+82 82
+83 83
+84 84
+85 85
+86 86
+87 87
+88 88
+89 89
+90 90
+91 91
+92 92
+93 93
+94 94
+95 95
+96 96
+97 97
+98 98
+99 99
+100 100
+101 101
+102 102
+103 103
+104 104
+105 105
+106 106
+107 107
+108 108
+109 109
+110 110
+111 111
+112 112
+113 113
+114 114
+115 115
+116 116
+117 117
+118 118
+119 119
+120 120
+121 121
+122 122
+123 123
+124 124
+125 125
+126 126
+127 127
+128 128
+129 129
+130 130
+131 131
+132 132
+133 133
+134 134
+135 135
+136 136
+137 137
+138 138
+139 139
+140 140
+141 141
+142 142
+143 143
+144 144
+145 145
+146 146
+147 147
+148 148
+149 149
+150 150
+151 151
+152 152
+153 153
+154 154
+155 155
+156 156
+157 157
+158 158
+159 159
+160 160
+161 161
+162 162
+163 163
+164 164
+165 165
+166 166
+167 167
+168 168
+169 169
+170 170
+171 171
+172 172
+173 173
+174 174
+175 175
+176 176
+177 177
+178 178
+179 179
+180 180
+181 181
+182 182
+183 183
+184 184
+185 185
+186 186
+187 187
+188 188
+189 189
+190 190
+191 191
+192 192
+193 193
+194 194
+195 195
+196 196
+197 197
+198 198
+199 199
+200 200
+201 201
+202 202
+203 203
+204 204
+205 205
+206 206
+207 207
+208 208
+209 209
+210 210
+211 211
+212 212
+213 213
+214 214
+215 215
+216 216
+217 217
+218 218
+219 219
+220 220
+221 221
+222 222
+223 223
+224 224
+225 225
+226 226
+227 227
+228 228
+229 229
+230 230
+231 231
+232 232
+233 233
+234 234
+235 235
+236 236
+237 237
+238 238
+239 239
+240 240
+241 241
+242 242
+243 243
+244 244
+245 245
+246 246
+247 247
+248 248
+249 249
+250 250
+251 251
+252 252
+253 253
+254 254
+255 255
+HS
+0 0
+1 1
+10 10
+100 100
+101 101
+102 102
+103 103
+104 104
+105 105
+106 106
+107 107
+108 108
+109 109
+11 11
+110 110
+111 111
+112 112
+113 113
+114 114
+115 115
+116 116
+117 117
+118 118
+119 119
+12 12
+120 120
+121 121
+122 122
+123 123
+124 124
+125 125
+126 126
+127 127
+128 128
+129 129
+13 13
+130 130
+131 131
+132 132
+133 133
+134 134
+135 135
+136 136
+137 137
+138 138
+139 139
+14 14
+140 140
+141 141
+142 142
+143 143
+144 144
+145 145
+146 146
+147 147
+148 148
+149 149
+15 15
+150 150
+151 151
+152 152
+153 153
+154 154
+155 155
+156 156
+157 157
+158 158
+159 159
+16 16
+160 160
+161 161
+162 162
+163 163
+164 164
+165 165
+166 166
+167 167
+168 168
+169 169
+17 17
+170 170
+171 171
+172 172
+173 173
+174 174
+175 175
+176 176
+177 177
+178 178
+179 179
+18 18
+180 180
+181 181
+182 182
+183 183
+184 184
+185 185
+186 186
+187 187
+188 188
+189 189
+19 19
+190 190
+191 191
+192 192
+193 193
+194 194
+195 195
+196 196
+197 197
+198 198
+199 199
+2 2
+20 20
+200 200
+201 201
+202 202
+203 203
+204 204
+205 205
+206 206
+207 207
+208 208
+209 209
+21 21
+210 210
+211 211
+212 212
+213 213
+214 214
+215 215
+216 216
+217 217
+218 218
+219 219
+22 22
+220 220
+221 221
+222 222
+223 223
+224 224
+225 225
+226 226
+227 227
+228 228
+229 229
+23 23
+230 230
+231 231
+232 232
+233 233
+234 234
+235 235
+236 236
+237 237
+238 238
+239 239
+24 24
+240 240
+241 241
+242 242
+243 243
+244 244
+245 245
+246 246
+247 247
+248 248
+249 249
+25 25
+250 250
+251 251
+252 252
+253 253
+254 254
+255 255
+26 26
+27 27
+28 28
+29 29
+3 3
+30 30
+31 31
+32 32
+33 33
+34 34
+35 35
+36 36
+37 37
+38 38
+39 39
+4 4
+40 40
+41 41
+42 42
+43 43
+44 44
+45 45
+46 46
+47 47
+48 48
+49 49
+5 5
+50 50
+51 51
+52 52
+53 53
+54 54
+55 55
+56 56
+57 57
+58 58
+59 59
+6 6
+60 60
+61 61
+62 62
+63 63
+64 64
+65 65
+66 66
+67 67
+68 68
+69 69
+7 7
+70 70
+71 71
+72 72
+73 73
+74 74
+75 75
+76 76
+77 77
+78 78
+79 79
+8 8
+80 80
+81 81
+82 82
+83 83
+84 84
+85 85
+86 86
+87 87
+88 88
+89 89
+9 9
+90 90
+91 91
+92 92
+93 93
+94 94
+95 95
+96 96
+97 97
+98 98
+99 99
+MY
+0 0
+1 1
+10 10
+100 100
+101 101
+102 102
+103 103
+104 104
+105 105
+106 106
+107 107
+108 108
+109 109
+11 11
+110 110
+111 111
+112 112
+113 113
+114 114
+115 115
+116 116
+117 117
+118 118
+119 119
+12 12
+120 120
+121 121
+122 122
+123 123
+124 124
+125 125
+126 126
+127 127
+128 128
+129 129
+13 13
+130 130
+131 131
+132 132
+133 133
+134 134
+135 135
+136 136
+137 137
+138 138
+139 139
+14 14
+140 140
+141 141
+142 142
+143 143
+144 144
+145 145
+146 146
+147 147
+148 148
+149 149
+15 15
+150 150
+151 151
+152 152
+153 153
+154 154
+155 155
+156 156
+157 157
+158 158
+159 159
+16 16
+160 160
+161 161
+162 162
+163 163
+164 164
+165 165
+166 166
+167 167
+168 168
+169 169
+17 17
+170 170
+171 171
+172 172
+173 173
+174 174
+175 175
+176 176
+177 177
+178 178
+179 179
+18 18
+180 180
+181 181
+182 182
+183 183
+184 184
+185 185
+186 186
+187 187
+188 188
+189 189
+19 19
+190 190
+191 191
+192 192
+193 193
+194 194
+195 195
+196 196
+197 197
+198 198
+199 199
+2 2
+20 20
+200 200
+201 201
+202 202
+203 203
+204 204
+205 205
+206 206
+207 207
+208 208
+209 209
+21 21
+210 210
+211 211
+212 212
+213 213
+214 214
+215 215
+216 216
+217 217
+218 218
+219 219
+22 22
+220 220
+221 221
+222 222
+223 223
+224 224
+225 225
+226 226
+227 227
+228 228
+229 229
+23 23
+230 230
+231 231
+232 232
+233 233
+234 234
+235 235
+236 236
+237 237
+238 238
+239 239
+24 24
+240 240
+241 241
+242 242
+243 243
+244 244
+245 245
+246 246
+247 247
+248 248
+249 249
+25 25
+250 250
+251 251
+252 252
+253 253
+254 254
+255 255
+26 26
+27 27
+28 28
+29 29
+3 3
+30 30
+31 31
+32 32
+33 33
+34 34
+35 35
+36 36
+37 37
+38 38
+39 39
+4 4
+40 40
+41 41
+42 42
+43 43
+44 44
+45 45
+46 46
+47 47
+48 48
+49 49
+5 5
+50 50
+51 51
+52 52
+53 53
+54 54
+55 55
+56 56
+57 57
+58 58
+59 59
+6 6
+60 60
+61 61
+62 62
+63 63
+64 64
+65 65
+66 66
+67 67
+68 68
+69 69
+7 7
+70 70
+71 71
+72 72
+73 73
+74 74
+75 75
+76 76
+77 77
+78 78
+79 79
+8 8
+80 80
+81 81
+82 82
+83 83
+84 84
+85 85
+86 86
+87 87
+88 88
+89 89
+9 9
+90 90
+91 91
+92 92
+93 93
+94 94
+95 95
+96 96
+97 97
+98 98
+99 99
diff --git a/plugin/handler_socket/regtest/test_01_lib/test03.pl b/plugin/handler_socket/regtest/test_01_lib/test03.pl
new file mode 100644
index 00000000000..a081786c132
--- /dev/null
+++ b/plugin/handler_socket/regtest/test_01_lib/test03.pl
@@ -0,0 +1,61 @@
+#!/usr/bin/perl
+
+# vim:sw=2:ai
+
+# test for binary cleanness (#1)
+
+BEGIN {
+ push @INC, "../common/";
+};
+
+use strict;
+use warnings;
+use hstest;
+
+my $dbh = hstest::init_testdb();
+my $table = 'hstesttbl';
+my $tablesize = 256;
+$dbh->do(
+ "create table $table (k varchar(30) primary key, v varchar(30) not null) " .
+ "engine = innodb default charset = binary");
+srand(999);
+
+my %valmap = ();
+
+print "WR\n";
+my $sth = $dbh->prepare("insert into $table values (?,?)");
+for (my $i = 0; $i < $tablesize; ++$i) {
+ my $k = "" . $i;
+ my $v = pack("C", $i);
+ my $vnum = unpack("C", $v);
+ print "$k $vnum\n";
+ $sth->execute($k, $v);
+ $valmap{$k} = $v;
+}
+
+my $hs = hstest::get_hs_connection();
+my $dbname = $hstest::conf{dbname};
+$hs->open_index(1, $dbname, $table, '', 'k,v');
+my $r = $hs->execute_single(1, '>=', [ '' ], 10000, 0);
+shift(@$r);
+print "HS\n";
+for (my $i = 0; $i < $tablesize; ++$i) {
+ my $k = $r->[$i * 2];
+ my $v = $r->[$i * 2 + 1];
+ my $vnum = unpack("C", $v);
+ print "$k $vnum\n";
+ print "MISMATCH\n" if ($k ne $vnum);
+ print "LEN\n" if (length($v) != 1);
+}
+undef $hs;
+
+print "MY\n";
+my $aref = $dbh->selectall_arrayref("select k,v from $table order by k");
+for my $row (@$aref) {
+ my ($k, $v) = @$row;
+ my $vnum = unpack("C", $v);
+ print "$k $vnum\n";
+ print "MISMATCH\n" if ($k ne $vnum);
+ print "LEN\n" if (length($v) != 1);
+}
+
diff --git a/plugin/handler_socket/regtest/test_01_lib/test04.expected b/plugin/handler_socket/regtest/test_01_lib/test04.expected
new file mode 100644
index 00000000000..ceeac4387b7
--- /dev/null
+++ b/plugin/handler_socket/regtest/test_01_lib/test04.expected
Binary files differ
diff --git a/plugin/handler_socket/regtest/test_01_lib/test04.pl b/plugin/handler_socket/regtest/test_01_lib/test04.pl
new file mode 100644
index 00000000000..52fe11364c8
--- /dev/null
+++ b/plugin/handler_socket/regtest/test_01_lib/test04.pl
@@ -0,0 +1,63 @@
+#!/usr/bin/perl
+
+# vim:sw=2:ai
+
+# test for binary cleanness (#2)
+
+BEGIN {
+ push @INC, "../common/";
+};
+
+use strict;
+use warnings;
+use hstest;
+
+my $dbh = hstest::init_testdb();
+my $table = 'hstesttbl';
+my $tablesize = 256;
+$dbh->do(
+ "create table $table (k varchar(30) primary key, v varchar(30) not null) " .
+ "engine = innodb default charset = binary");
+srand(999);
+
+my %valmap = ();
+
+print "WR\n";
+my $sth = $dbh->prepare("insert into $table values (?,?)");
+for (my $i = 0; $i < $tablesize; ++$i) {
+ my $k = "" . $i;
+ my $v = pack("C", $i);
+ my $vnum = unpack("C", $v);
+ print "$k $vnum\n";
+ $sth->execute($k, "a" . $v . "a");
+ $valmap{$k} = $v;
+}
+
+my $hs = hstest::get_hs_connection();
+my $dbname = $hstest::conf{dbname};
+$hs->open_index(1, $dbname, $table, '', 'k,v');
+my $r = $hs->execute_single(1, '>=', [ '' ], 10000, 0);
+shift(@$r);
+print "HS\n";
+for (my $i = 0; $i < $tablesize; ++$i) {
+ my $k = $r->[$i * 2];
+ my $v = $r->[$i * 2 + 1];
+ my $len = length($v);
+ my $vnum = unpack("C", substr($v, 1, 1));
+ print "$k $vnum $len [$v]\n";
+ print "MISMATCH\n" if ($k ne $vnum);
+ print "LEN\n" if $len != 3;
+}
+undef $hs;
+
+print "MY\n";
+my $aref = $dbh->selectall_arrayref("select k,v from $table order by k");
+for my $row (@$aref) {
+ my ($k, $v) = @$row;
+ my $len = length($v);
+ my $vnum = unpack("C", substr($v, 1, 1));
+ print "$k $vnum $len [$v]\n";
+ print "MISMATCH\n" if ($k ne $vnum);
+ print "LEN\n" if $len != 3;
+}
+
diff --git a/plugin/handler_socket/regtest/test_01_lib/test05.expected b/plugin/handler_socket/regtest/test_01_lib/test05.expected
new file mode 100644
index 00000000000..6a86a446e66
--- /dev/null
+++ b/plugin/handler_socket/regtest/test_01_lib/test05.expected
@@ -0,0 +1,771 @@
+WR
+0 [null]
+1 1
+2 [null]
+3 3
+4 [null]
+5 5
+6 [null]
+7 7
+8 [null]
+9 9
+10 [null]
+11 11
+12 [null]
+13 13
+14 [null]
+15 15
+16 [null]
+17 17
+18 [null]
+19 19
+20 [null]
+21 21
+22 [null]
+23 23
+24 [null]
+25 25
+26 [null]
+27 27
+28 [null]
+29 29
+30 [null]
+31 31
+32 [null]
+33 33
+34 [null]
+35 35
+36 [null]
+37 37
+38 [null]
+39 39
+40 [null]
+41 41
+42 [null]
+43 43
+44 [null]
+45 45
+46 [null]
+47 47
+48 [null]
+49 49
+50 [null]
+51 51
+52 [null]
+53 53
+54 [null]
+55 55
+56 [null]
+57 57
+58 [null]
+59 59
+60 [null]
+61 61
+62 [null]
+63 63
+64 [null]
+65 65
+66 [null]
+67 67
+68 [null]
+69 69
+70 [null]
+71 71
+72 [null]
+73 73
+74 [null]
+75 75
+76 [null]
+77 77
+78 [null]
+79 79
+80 [null]
+81 81
+82 [null]
+83 83
+84 [null]
+85 85
+86 [null]
+87 87
+88 [null]
+89 89
+90 [null]
+91 91
+92 [null]
+93 93
+94 [null]
+95 95
+96 [null]
+97 97
+98 [null]
+99 99
+100 [null]
+101 101
+102 [null]
+103 103
+104 [null]
+105 105
+106 [null]
+107 107
+108 [null]
+109 109
+110 [null]
+111 111
+112 [null]
+113 113
+114 [null]
+115 115
+116 [null]
+117 117
+118 [null]
+119 119
+120 [null]
+121 121
+122 [null]
+123 123
+124 [null]
+125 125
+126 [null]
+127 127
+128 [null]
+129 129
+130 [null]
+131 131
+132 [null]
+133 133
+134 [null]
+135 135
+136 [null]
+137 137
+138 [null]
+139 139
+140 [null]
+141 141
+142 [null]
+143 143
+144 [null]
+145 145
+146 [null]
+147 147
+148 [null]
+149 149
+150 [null]
+151 151
+152 [null]
+153 153
+154 [null]
+155 155
+156 [null]
+157 157
+158 [null]
+159 159
+160 [null]
+161 161
+162 [null]
+163 163
+164 [null]
+165 165
+166 [null]
+167 167
+168 [null]
+169 169
+170 [null]
+171 171
+172 [null]
+173 173
+174 [null]
+175 175
+176 [null]
+177 177
+178 [null]
+179 179
+180 [null]
+181 181
+182 [null]
+183 183
+184 [null]
+185 185
+186 [null]
+187 187
+188 [null]
+189 189
+190 [null]
+191 191
+192 [null]
+193 193
+194 [null]
+195 195
+196 [null]
+197 197
+198 [null]
+199 199
+200 [null]
+201 201
+202 [null]
+203 203
+204 [null]
+205 205
+206 [null]
+207 207
+208 [null]
+209 209
+210 [null]
+211 211
+212 [null]
+213 213
+214 [null]
+215 215
+216 [null]
+217 217
+218 [null]
+219 219
+220 [null]
+221 221
+222 [null]
+223 223
+224 [null]
+225 225
+226 [null]
+227 227
+228 [null]
+229 229
+230 [null]
+231 231
+232 [null]
+233 233
+234 [null]
+235 235
+236 [null]
+237 237
+238 [null]
+239 239
+240 [null]
+241 241
+242 [null]
+243 243
+244 [null]
+245 245
+246 [null]
+247 247
+248 [null]
+249 249
+250 [null]
+251 251
+252 [null]
+253 253
+254 [null]
+255 255
+HS
+0 [null]
+1 1
+10 [null]
+100 [null]
+101 101
+102 [null]
+103 103
+104 [null]
+105 105
+106 [null]
+107 107
+108 [null]
+109 109
+11 11
+110 [null]
+111 111
+112 [null]
+113 113
+114 [null]
+115 115
+116 [null]
+117 117
+118 [null]
+119 119
+12 [null]
+120 [null]
+121 121
+122 [null]
+123 123
+124 [null]
+125 125
+126 [null]
+127 127
+128 [null]
+129 129
+13 13
+130 [null]
+131 131
+132 [null]
+133 133
+134 [null]
+135 135
+136 [null]
+137 137
+138 [null]
+139 139
+14 [null]
+140 [null]
+141 141
+142 [null]
+143 143
+144 [null]
+145 145
+146 [null]
+147 147
+148 [null]
+149 149
+15 15
+150 [null]
+151 151
+152 [null]
+153 153
+154 [null]
+155 155
+156 [null]
+157 157
+158 [null]
+159 159
+16 [null]
+160 [null]
+161 161
+162 [null]
+163 163
+164 [null]
+165 165
+166 [null]
+167 167
+168 [null]
+169 169
+17 17
+170 [null]
+171 171
+172 [null]
+173 173
+174 [null]
+175 175
+176 [null]
+177 177
+178 [null]
+179 179
+18 [null]
+180 [null]
+181 181
+182 [null]
+183 183
+184 [null]
+185 185
+186 [null]
+187 187
+188 [null]
+189 189
+19 19
+190 [null]
+191 191
+192 [null]
+193 193
+194 [null]
+195 195
+196 [null]
+197 197
+198 [null]
+199 199
+2 [null]
+20 [null]
+200 [null]
+201 201
+202 [null]
+203 203
+204 [null]
+205 205
+206 [null]
+207 207
+208 [null]
+209 209
+21 21
+210 [null]
+211 211
+212 [null]
+213 213
+214 [null]
+215 215
+216 [null]
+217 217
+218 [null]
+219 219
+22 [null]
+220 [null]
+221 221
+222 [null]
+223 223
+224 [null]
+225 225
+226 [null]
+227 227
+228 [null]
+229 229
+23 23
+230 [null]
+231 231
+232 [null]
+233 233
+234 [null]
+235 235
+236 [null]
+237 237
+238 [null]
+239 239
+24 [null]
+240 [null]
+241 241
+242 [null]
+243 243
+244 [null]
+245 245
+246 [null]
+247 247
+248 [null]
+249 249
+25 25
+250 [null]
+251 251
+252 [null]
+253 253
+254 [null]
+255 255
+26 [null]
+27 27
+28 [null]
+29 29
+3 3
+30 [null]
+31 31
+32 [null]
+33 33
+34 [null]
+35 35
+36 [null]
+37 37
+38 [null]
+39 39
+4 [null]
+40 [null]
+41 41
+42 [null]
+43 43
+44 [null]
+45 45
+46 [null]
+47 47
+48 [null]
+49 49
+5 5
+50 [null]
+51 51
+52 [null]
+53 53
+54 [null]
+55 55
+56 [null]
+57 57
+58 [null]
+59 59
+6 [null]
+60 [null]
+61 61
+62 [null]
+63 63
+64 [null]
+65 65
+66 [null]
+67 67
+68 [null]
+69 69
+7 7
+70 [null]
+71 71
+72 [null]
+73 73
+74 [null]
+75 75
+76 [null]
+77 77
+78 [null]
+79 79
+8 [null]
+80 [null]
+81 81
+82 [null]
+83 83
+84 [null]
+85 85
+86 [null]
+87 87
+88 [null]
+89 89
+9 9
+90 [null]
+91 91
+92 [null]
+93 93
+94 [null]
+95 95
+96 [null]
+97 97
+98 [null]
+99 99
+MY
+0 [null]
+1 1
+10 [null]
+100 [null]
+101 101
+102 [null]
+103 103
+104 [null]
+105 105
+106 [null]
+107 107
+108 [null]
+109 109
+11 11
+110 [null]
+111 111
+112 [null]
+113 113
+114 [null]
+115 115
+116 [null]
+117 117
+118 [null]
+119 119
+12 [null]
+120 [null]
+121 121
+122 [null]
+123 123
+124 [null]
+125 125
+126 [null]
+127 127
+128 [null]
+129 129
+13 13
+130 [null]
+131 131
+132 [null]
+133 133
+134 [null]
+135 135
+136 [null]
+137 137
+138 [null]
+139 139
+14 [null]
+140 [null]
+141 141
+142 [null]
+143 143
+144 [null]
+145 145
+146 [null]
+147 147
+148 [null]
+149 149
+15 15
+150 [null]
+151 151
+152 [null]
+153 153
+154 [null]
+155 155
+156 [null]
+157 157
+158 [null]
+159 159
+16 [null]
+160 [null]
+161 161
+162 [null]
+163 163
+164 [null]
+165 165
+166 [null]
+167 167
+168 [null]
+169 169
+17 17
+170 [null]
+171 171
+172 [null]
+173 173
+174 [null]
+175 175
+176 [null]
+177 177
+178 [null]
+179 179
+18 [null]
+180 [null]
+181 181
+182 [null]
+183 183
+184 [null]
+185 185
+186 [null]
+187 187
+188 [null]
+189 189
+19 19
+190 [null]
+191 191
+192 [null]
+193 193
+194 [null]
+195 195
+196 [null]
+197 197
+198 [null]
+199 199
+2 [null]
+20 [null]
+200 [null]
+201 201
+202 [null]
+203 203
+204 [null]
+205 205
+206 [null]
+207 207
+208 [null]
+209 209
+21 21
+210 [null]
+211 211
+212 [null]
+213 213
+214 [null]
+215 215
+216 [null]
+217 217
+218 [null]
+219 219
+22 [null]
+220 [null]
+221 221
+222 [null]
+223 223
+224 [null]
+225 225
+226 [null]
+227 227
+228 [null]
+229 229
+23 23
+230 [null]
+231 231
+232 [null]
+233 233
+234 [null]
+235 235
+236 [null]
+237 237
+238 [null]
+239 239
+24 [null]
+240 [null]
+241 241
+242 [null]
+243 243
+244 [null]
+245 245
+246 [null]
+247 247
+248 [null]
+249 249
+25 25
+250 [null]
+251 251
+252 [null]
+253 253
+254 [null]
+255 255
+26 [null]
+27 27
+28 [null]
+29 29
+3 3
+30 [null]
+31 31
+32 [null]
+33 33
+34 [null]
+35 35
+36 [null]
+37 37
+38 [null]
+39 39
+4 [null]
+40 [null]
+41 41
+42 [null]
+43 43
+44 [null]
+45 45
+46 [null]
+47 47
+48 [null]
+49 49
+5 5
+50 [null]
+51 51
+52 [null]
+53 53
+54 [null]
+55 55
+56 [null]
+57 57
+58 [null]
+59 59
+6 [null]
+60 [null]
+61 61
+62 [null]
+63 63
+64 [null]
+65 65
+66 [null]
+67 67
+68 [null]
+69 69
+7 7
+70 [null]
+71 71
+72 [null]
+73 73
+74 [null]
+75 75
+76 [null]
+77 77
+78 [null]
+79 79
+8 [null]
+80 [null]
+81 81
+82 [null]
+83 83
+84 [null]
+85 85
+86 [null]
+87 87
+88 [null]
+89 89
+9 9
+90 [null]
+91 91
+92 [null]
+93 93
+94 [null]
+95 95
+96 [null]
+97 97
+98 [null]
+99 99
diff --git a/plugin/handler_socket/regtest/test_01_lib/test05.pl b/plugin/handler_socket/regtest/test_01_lib/test05.pl
new file mode 100644
index 00000000000..10b1a0805a0
--- /dev/null
+++ b/plugin/handler_socket/regtest/test_01_lib/test05.pl
@@ -0,0 +1,59 @@
+#!/usr/bin/perl
+
+# vim:sw=2:ai
+
+# test for binary cleanness (#3)
+
+BEGIN {
+ push @INC, "../common/";
+};
+
+use strict;
+use warnings;
+use hstest;
+
+my $dbh = hstest::init_testdb();
+my $table = 'hstesttbl';
+my $tablesize = 256;
+$dbh->do(
+ "create table $table (k varchar(30) primary key, v varchar(30)) " .
+ "engine = innodb default charset = binary");
+srand(999);
+
+my %valmap = ();
+
+print "WR\n";
+my $sth = $dbh->prepare("insert into $table values (?,?)");
+for (my $i = 0; $i < $tablesize; ++$i) {
+ my $k = "" . $i;
+ my $v = ($i % 2 == 1) ? $i : undef;
+ $sth->execute($k, $v);
+ $v = "[null]" if !defined($v);
+ print "$k $v\n";
+ $valmap{$k} = $v;
+}
+
+my $hs = hstest::get_hs_connection();
+my $dbname = $hstest::conf{dbname};
+$hs->open_index(1, $dbname, $table, '', 'k,v');
+my $r = $hs->execute_single(1, '>=', [ '' ], 10000, 0);
+shift(@$r);
+print "HS\n";
+for (my $i = 0; $i < $tablesize; ++$i) {
+ my $k = $r->[$i * 2];
+ my $v = $r->[$i * 2 + 1];
+ $v = "[null]" if !defined($v);
+ print "$k $v\n";
+ print "MISMATCH\n" if ($valmap{$k} ne $v);
+}
+undef $hs;
+
+print "MY\n";
+my $aref = $dbh->selectall_arrayref("select k,v from $table order by k");
+for my $row (@$aref) {
+ my ($k, $v) = @$row;
+ $v = "[null]" if !defined($v);
+ print "$k $v\n";
+ print "MISMATCH\n" if ($valmap{$k} ne $v);
+}
+
diff --git a/plugin/handler_socket/regtest/test_01_lib/test06.expected b/plugin/handler_socket/regtest/test_01_lib/test06.expected
new file mode 100644
index 00000000000..b376c4af0be
--- /dev/null
+++ b/plugin/handler_socket/regtest/test_01_lib/test06.expected
@@ -0,0 +1,644 @@
+HSINSERTDUMP_TABLE
+0 v1_0 v2_0
+1 v1_1 v2_1
+10 v1_10 v2_10
+100 v1_100 v2_100
+101 v1_101 v2_101
+102 v1_102 v2_102
+103 v1_103 v2_103
+104 v1_104 v2_104
+105 v1_105 v2_105
+106 v1_106 v2_106
+107 v1_107 v2_107
+108 v1_108 v2_108
+109 v1_109 v2_109
+11 v1_11 v2_11
+110 v1_110 v2_110
+111 v1_111 v2_111
+112 v1_112 v2_112
+113 v1_113 v2_113
+114 v1_114 v2_114
+115 v1_115 v2_115
+116 v1_116 v2_116
+117 v1_117 v2_117
+118 v1_118 v2_118
+119 v1_119 v2_119
+12 v1_12 v2_12
+120 v1_120 v2_120
+121 v1_121 v2_121
+122 v1_122 v2_122
+123 v1_123 v2_123
+124 v1_124 v2_124
+125 v1_125 v2_125
+126 v1_126 v2_126
+127 v1_127 v2_127
+128 v1_128 v2_128
+129 v1_129 v2_129
+13 v1_13 v2_13
+130 v1_130 v2_130
+131 v1_131 v2_131
+132 v1_132 v2_132
+133 v1_133 v2_133
+134 v1_134 v2_134
+135 v1_135 v2_135
+136 v1_136 v2_136
+137 v1_137 v2_137
+138 v1_138 v2_138
+139 v1_139 v2_139
+14 v1_14 v2_14
+140 v1_140 v2_140
+141 v1_141 v2_141
+142 v1_142 v2_142
+143 v1_143 v2_143
+144 v1_144 v2_144
+145 v1_145 v2_145
+146 v1_146 v2_146
+147 v1_147 v2_147
+148 v1_148 v2_148
+149 v1_149 v2_149
+15 v1_15 v2_15
+150 v1_150 v2_150
+151 v1_151 v2_151
+152 v1_152 v2_152
+153 v1_153 v2_153
+154 v1_154 v2_154
+155 v1_155 v2_155
+156 v1_156 v2_156
+157 v1_157 v2_157
+158 v1_158 v2_158
+159 v1_159 v2_159
+16 v1_16 v2_16
+160 v1_160 v2_160
+161 v1_161 v2_161
+162 v1_162 v2_162
+163 v1_163 v2_163
+164 v1_164 v2_164
+165 v1_165 v2_165
+166 v1_166 v2_166
+167 v1_167 v2_167
+168 v1_168 v2_168
+169 v1_169 v2_169
+17 v1_17 v2_17
+170 v1_170 v2_170
+171 v1_171 v2_171
+172 v1_172 v2_172
+173 v1_173 v2_173
+174 v1_174 v2_174
+175 v1_175 v2_175
+176 v1_176 v2_176
+177 v1_177 v2_177
+178 v1_178 v2_178
+179 v1_179 v2_179
+18 v1_18 v2_18
+180 v1_180 v2_180
+181 v1_181 v2_181
+182 v1_182 v2_182
+183 v1_183 v2_183
+184 v1_184 v2_184
+185 v1_185 v2_185
+186 v1_186 v2_186
+187 v1_187 v2_187
+188 v1_188 v2_188
+189 v1_189 v2_189
+19 v1_19 v2_19
+190 v1_190 v2_190
+191 v1_191 v2_191
+192 v1_192 v2_192
+193 v1_193 v2_193
+194 v1_194 v2_194
+195 v1_195 v2_195
+196 v1_196 v2_196
+197 v1_197 v2_197
+198 v1_198 v2_198
+199 v1_199 v2_199
+2 v1_2 v2_2
+20 v1_20 v2_20
+200 v1_200 v2_200
+201 v1_201 v2_201
+202 v1_202 v2_202
+203 v1_203 v2_203
+204 v1_204 v2_204
+205 v1_205 v2_205
+206 v1_206 v2_206
+207 v1_207 v2_207
+208 v1_208 v2_208
+209 v1_209 v2_209
+21 v1_21 v2_21
+210 v1_210 v2_210
+211 v1_211 v2_211
+212 v1_212 v2_212
+213 v1_213 v2_213
+214 v1_214 v2_214
+215 v1_215 v2_215
+216 v1_216 v2_216
+217 v1_217 v2_217
+218 v1_218 v2_218
+219 v1_219 v2_219
+22 v1_22 v2_22
+220 v1_220 v2_220
+221 v1_221 v2_221
+222 v1_222 v2_222
+223 v1_223 v2_223
+224 v1_224 v2_224
+225 v1_225 v2_225
+226 v1_226 v2_226
+227 v1_227 v2_227
+228 v1_228 v2_228
+229 v1_229 v2_229
+23 v1_23 v2_23
+230 v1_230 v2_230
+231 v1_231 v2_231
+232 v1_232 v2_232
+233 v1_233 v2_233
+234 v1_234 v2_234
+235 v1_235 v2_235
+236 v1_236 v2_236
+237 v1_237 v2_237
+238 v1_238 v2_238
+239 v1_239 v2_239
+24 v1_24 v2_24
+240 v1_240 v2_240
+241 v1_241 v2_241
+242 v1_242 v2_242
+243 v1_243 v2_243
+244 v1_244 v2_244
+245 v1_245 v2_245
+246 v1_246 v2_246
+247 v1_247 v2_247
+248 v1_248 v2_248
+249 v1_249 v2_249
+25 v1_25 v2_25
+250 v1_250 v2_250
+251 v1_251 v2_251
+252 v1_252 v2_252
+253 v1_253 v2_253
+254 v1_254 v2_254
+255 v1_255 v2_255
+26 v1_26 v2_26
+27 v1_27 v2_27
+28 v1_28 v2_28
+29 v1_29 v2_29
+3 v1_3 v2_3
+30 v1_30 v2_30
+31 v1_31 v2_31
+32 v1_32 v2_32
+33 v1_33 v2_33
+34 v1_34 v2_34
+35 v1_35 v2_35
+36 v1_36 v2_36
+37 v1_37 v2_37
+38 v1_38 v2_38
+39 v1_39 v2_39
+4 v1_4 v2_4
+40 v1_40 v2_40
+41 v1_41 v2_41
+42 v1_42 v2_42
+43 v1_43 v2_43
+44 v1_44 v2_44
+45 v1_45 v2_45
+46 v1_46 v2_46
+47 v1_47 v2_47
+48 v1_48 v2_48
+49 v1_49 v2_49
+5 v1_5 v2_5
+50 v1_50 v2_50
+51 v1_51 v2_51
+52 v1_52 v2_52
+53 v1_53 v2_53
+54 v1_54 v2_54
+55 v1_55 v2_55
+56 v1_56 v2_56
+57 v1_57 v2_57
+58 v1_58 v2_58
+59 v1_59 v2_59
+6 v1_6 v2_6
+60 v1_60 v2_60
+61 v1_61 v2_61
+62 v1_62 v2_62
+63 v1_63 v2_63
+64 v1_64 v2_64
+65 v1_65 v2_65
+66 v1_66 v2_66
+67 v1_67 v2_67
+68 v1_68 v2_68
+69 v1_69 v2_69
+7 v1_7 v2_7
+70 v1_70 v2_70
+71 v1_71 v2_71
+72 v1_72 v2_72
+73 v1_73 v2_73
+74 v1_74 v2_74
+75 v1_75 v2_75
+76 v1_76 v2_76
+77 v1_77 v2_77
+78 v1_78 v2_78
+79 v1_79 v2_79
+8 v1_8 v2_8
+80 v1_80 v2_80
+81 v1_81 v2_81
+82 v1_82 v2_82
+83 v1_83 v2_83
+84 v1_84 v2_84
+85 v1_85 v2_85
+86 v1_86 v2_86
+87 v1_87 v2_87
+88 v1_88 v2_88
+89 v1_89 v2_89
+9 v1_9 v2_9
+90 v1_90 v2_90
+91 v1_91 v2_91
+92 v1_92 v2_92
+93 v1_93 v2_93
+94 v1_94 v2_94
+95 v1_95 v2_95
+96 v1_96 v2_96
+97 v1_97 v2_97
+98 v1_98 v2_98
+99 v1_99 v2_99
+HSUPDATEDUMP_TABLE
+0 mod_0 v2_0
+1 mod_1 v2_1
+10 mod_10 v2_10
+100 mod_100 v2_100
+101 mod_101 v2_101
+102 mod_102 v2_102
+103 mod_103 v2_103
+104 mod_104 v2_104
+105 mod_105 v2_105
+106 mod_106 v2_106
+107 mod_107 v2_107
+108 mod_108 v2_108
+109 mod_109 v2_109
+11 mod_11 v2_11
+110 mod_110 v2_110
+111 mod_111 v2_111
+112 mod_112 v2_112
+113 mod_113 v2_113
+114 mod_114 v2_114
+115 mod_115 v2_115
+116 mod_116 v2_116
+117 mod_117 v2_117
+118 mod_118 v2_118
+119 mod_119 v2_119
+12 mod_12 v2_12
+120 mod_120 v2_120
+121 mod_121 v2_121
+122 mod_122 v2_122
+123 mod_123 v2_123
+124 mod_124 v2_124
+125 mod_125 v2_125
+126 mod_126 v2_126
+127 mod_127 v2_127
+128 mod_128 v2_128
+129 mod_129 v2_129
+13 mod_13 v2_13
+130 mod_130 v2_130
+131 mod_131 v2_131
+132 mod_132 v2_132
+133 mod_133 v2_133
+134 mod_134 v2_134
+135 mod_135 v2_135
+136 mod_136 v2_136
+137 mod_137 v2_137
+138 mod_138 v2_138
+139 mod_139 v2_139
+14 mod_14 v2_14
+140 mod_140 v2_140
+141 mod_141 v2_141
+142 mod_142 v2_142
+143 mod_143 v2_143
+144 mod_144 v2_144
+145 mod_145 v2_145
+146 mod_146 v2_146
+147 mod_147 v2_147
+148 mod_148 v2_148
+149 mod_149 v2_149
+15 mod_15 v2_15
+150 mod_150 v2_150
+151 mod_151 v2_151
+152 mod_152 v2_152
+153 mod_153 v2_153
+154 mod_154 v2_154
+155 mod_155 v2_155
+156 mod_156 v2_156
+157 mod_157 v2_157
+158 mod_158 v2_158
+159 mod_159 v2_159
+16 mod_16 v2_16
+160 mod_160 v2_160
+161 mod_161 v2_161
+162 mod_162 v2_162
+163 mod_163 v2_163
+164 mod_164 v2_164
+165 mod_165 v2_165
+166 mod_166 v2_166
+167 mod_167 v2_167
+168 mod_168 v2_168
+169 mod_169 v2_169
+17 mod_17 v2_17
+170 mod_170 v2_170
+171 mod_171 v2_171
+172 mod_172 v2_172
+173 mod_173 v2_173
+174 mod_174 v2_174
+175 mod_175 v2_175
+176 mod_176 v2_176
+177 mod_177 v2_177
+178 mod_178 v2_178
+179 mod_179 v2_179
+18 mod_18 v2_18
+180 mod_180 v2_180
+181 mod_181 v2_181
+182 mod_182 v2_182
+183 mod_183 v2_183
+184 mod_184 v2_184
+185 mod_185 v2_185
+186 mod_186 v2_186
+187 mod_187 v2_187
+188 mod_188 v2_188
+189 mod_189 v2_189
+19 mod_19 v2_19
+190 mod_190 v2_190
+191 mod_191 v2_191
+192 mod_192 v2_192
+193 mod_193 v2_193
+194 mod_194 v2_194
+195 mod_195 v2_195
+196 mod_196 v2_196
+197 mod_197 v2_197
+198 mod_198 v2_198
+199 mod_199 v2_199
+2 mod_2 v2_2
+20 mod_20 v2_20
+200 mod_200 v2_200
+201 mod_201 v2_201
+202 mod_202 v2_202
+203 mod_203 v2_203
+204 mod_204 v2_204
+205 mod_205 v2_205
+206 mod_206 v2_206
+207 mod_207 v2_207
+208 mod_208 v2_208
+209 mod_209 v2_209
+21 mod_21 v2_21
+210 mod_210 v2_210
+211 mod_211 v2_211
+212 mod_212 v2_212
+213 mod_213 v2_213
+214 mod_214 v2_214
+215 mod_215 v2_215
+216 mod_216 v2_216
+217 mod_217 v2_217
+218 mod_218 v2_218
+219 mod_219 v2_219
+22 mod_22 v2_22
+220 mod_220 v2_220
+221 mod_221 v2_221
+222 mod_222 v2_222
+223 mod_223 v2_223
+224 mod_224 v2_224
+225 mod_225 v2_225
+226 mod_226 v2_226
+227 mod_227 v2_227
+228 mod_228 v2_228
+229 mod_229 v2_229
+23 mod_23 v2_23
+230 mod_230 v2_230
+231 mod_231 v2_231
+232 mod_232 v2_232
+233 mod_233 v2_233
+234 mod_234 v2_234
+235 mod_235 v2_235
+236 mod_236 v2_236
+237 mod_237 v2_237
+238 mod_238 v2_238
+239 mod_239 v2_239
+24 mod_24 v2_24
+240 mod_240 v2_240
+241 mod_241 v2_241
+242 mod_242 v2_242
+243 mod_243 v2_243
+244 mod_244 v2_244
+245 mod_245 v2_245
+246 mod_246 v2_246
+247 mod_247 v2_247
+248 mod_248 v2_248
+249 mod_249 v2_249
+25 mod_25 v2_25
+250 mod_250 v2_250
+251 mod_251 v2_251
+252 mod_252 v2_252
+253 mod_253 v2_253
+254 mod_254 v2_254
+255 mod_255 v2_255
+26 mod_26 v2_26
+27 mod_27 v2_27
+28 mod_28 v2_28
+29 mod_29 v2_29
+3 mod_3 v2_3
+30 mod_30 v2_30
+31 mod_31 v2_31
+32 mod_32 v2_32
+33 mod_33 v2_33
+34 mod_34 v2_34
+35 mod_35 v2_35
+36 mod_36 v2_36
+37 mod_37 v2_37
+38 mod_38 v2_38
+39 mod_39 v2_39
+4 mod_4 v2_4
+40 mod_40 v2_40
+41 mod_41 v2_41
+42 mod_42 v2_42
+43 mod_43 v2_43
+44 mod_44 v2_44
+45 mod_45 v2_45
+46 mod_46 v2_46
+47 mod_47 v2_47
+48 mod_48 v2_48
+49 mod_49 v2_49
+5 mod_5 v2_5
+50 mod_50 v2_50
+51 mod_51 v2_51
+52 mod_52 v2_52
+53 mod_53 v2_53
+54 mod_54 v2_54
+55 mod_55 v2_55
+56 mod_56 v2_56
+57 mod_57 v2_57
+58 mod_58 v2_58
+59 mod_59 v2_59
+6 mod_6 v2_6
+60 mod_60 v2_60
+61 mod_61 v2_61
+62 mod_62 v2_62
+63 mod_63 v2_63
+64 mod_64 v2_64
+65 mod_65 v2_65
+66 mod_66 v2_66
+67 mod_67 v2_67
+68 mod_68 v2_68
+69 mod_69 v2_69
+7 mod_7 v2_7
+70 mod_70 v2_70
+71 mod_71 v2_71
+72 mod_72 v2_72
+73 mod_73 v2_73
+74 mod_74 v2_74
+75 mod_75 v2_75
+76 mod_76 v2_76
+77 mod_77 v2_77
+78 mod_78 v2_78
+79 mod_79 v2_79
+8 mod_8 v2_8
+80 mod_80 v2_80
+81 mod_81 v2_81
+82 mod_82 v2_82
+83 mod_83 v2_83
+84 mod_84 v2_84
+85 mod_85 v2_85
+86 mod_86 v2_86
+87 mod_87 v2_87
+88 mod_88 v2_88
+89 mod_89 v2_89
+9 mod_9 v2_9
+90 mod_90 v2_90
+91 mod_91 v2_91
+92 mod_92 v2_92
+93 mod_93 v2_93
+94 mod_94 v2_94
+95 mod_95 v2_95
+96 mod_96 v2_96
+97 mod_97 v2_97
+98 mod_98 v2_98
+99 mod_99 v2_99
+HSDELETE
+DUMP_TABLE
+1 mod_1 v2_1
+101 mod_101 v2_101
+103 mod_103 v2_103
+105 mod_105 v2_105
+107 mod_107 v2_107
+109 mod_109 v2_109
+11 mod_11 v2_11
+111 mod_111 v2_111
+113 mod_113 v2_113
+115 mod_115 v2_115
+117 mod_117 v2_117
+119 mod_119 v2_119
+121 mod_121 v2_121
+123 mod_123 v2_123
+125 mod_125 v2_125
+127 mod_127 v2_127
+129 mod_129 v2_129
+13 mod_13 v2_13
+131 mod_131 v2_131
+133 mod_133 v2_133
+135 mod_135 v2_135
+137 mod_137 v2_137
+139 mod_139 v2_139
+141 mod_141 v2_141
+143 mod_143 v2_143
+145 mod_145 v2_145
+147 mod_147 v2_147
+149 mod_149 v2_149
+15 mod_15 v2_15
+151 mod_151 v2_151
+153 mod_153 v2_153
+155 mod_155 v2_155
+157 mod_157 v2_157
+159 mod_159 v2_159
+161 mod_161 v2_161
+163 mod_163 v2_163
+165 mod_165 v2_165
+167 mod_167 v2_167
+169 mod_169 v2_169
+17 mod_17 v2_17
+171 mod_171 v2_171
+173 mod_173 v2_173
+175 mod_175 v2_175
+177 mod_177 v2_177
+179 mod_179 v2_179
+181 mod_181 v2_181
+183 mod_183 v2_183
+185 mod_185 v2_185
+187 mod_187 v2_187
+189 mod_189 v2_189
+19 mod_19 v2_19
+191 mod_191 v2_191
+193 mod_193 v2_193
+195 mod_195 v2_195
+197 mod_197 v2_197
+199 mod_199 v2_199
+201 mod_201 v2_201
+203 mod_203 v2_203
+205 mod_205 v2_205
+207 mod_207 v2_207
+209 mod_209 v2_209
+21 mod_21 v2_21
+211 mod_211 v2_211
+213 mod_213 v2_213
+215 mod_215 v2_215
+217 mod_217 v2_217
+219 mod_219 v2_219
+221 mod_221 v2_221
+223 mod_223 v2_223
+225 mod_225 v2_225
+227 mod_227 v2_227
+229 mod_229 v2_229
+23 mod_23 v2_23
+231 mod_231 v2_231
+233 mod_233 v2_233
+235 mod_235 v2_235
+237 mod_237 v2_237
+239 mod_239 v2_239
+241 mod_241 v2_241
+243 mod_243 v2_243
+245 mod_245 v2_245
+247 mod_247 v2_247
+249 mod_249 v2_249
+25 mod_25 v2_25
+251 mod_251 v2_251
+253 mod_253 v2_253
+255 mod_255 v2_255
+27 mod_27 v2_27
+29 mod_29 v2_29
+3 mod_3 v2_3
+31 mod_31 v2_31
+33 mod_33 v2_33
+35 mod_35 v2_35
+37 mod_37 v2_37
+39 mod_39 v2_39
+41 mod_41 v2_41
+43 mod_43 v2_43
+45 mod_45 v2_45
+47 mod_47 v2_47
+49 mod_49 v2_49
+5 mod_5 v2_5
+51 mod_51 v2_51
+53 mod_53 v2_53
+55 mod_55 v2_55
+57 mod_57 v2_57
+59 mod_59 v2_59
+61 mod_61 v2_61
+63 mod_63 v2_63
+65 mod_65 v2_65
+67 mod_67 v2_67
+69 mod_69 v2_69
+7 mod_7 v2_7
+71 mod_71 v2_71
+73 mod_73 v2_73
+75 mod_75 v2_75
+77 mod_77 v2_77
+79 mod_79 v2_79
+81 mod_81 v2_81
+83 mod_83 v2_83
+85 mod_85 v2_85
+87 mod_87 v2_87
+89 mod_89 v2_89
+9 mod_9 v2_9
+91 mod_91 v2_91
+93 mod_93 v2_93
+95 mod_95 v2_95
+97 mod_97 v2_97
+99 mod_99 v2_99
diff --git a/plugin/handler_socket/regtest/test_01_lib/test06.pl b/plugin/handler_socket/regtest/test_01_lib/test06.pl
new file mode 100644
index 00000000000..fb0549f2295
--- /dev/null
+++ b/plugin/handler_socket/regtest/test_01_lib/test06.pl
@@ -0,0 +1,90 @@
+#!/usr/bin/perl
+
+# vim:sw=2:ai
+
+# test for insert/update/delete
+
+BEGIN {
+ push @INC, "../common/";
+};
+
+use strict;
+use warnings;
+use hstest;
+
+my $dbh = hstest::init_testdb();
+my $table = 'hstesttbl';
+my $tablesize = 256;
+$dbh->do(
+ "create table $table (" .
+ "k varchar(30) primary key, " .
+ "v1 varchar(30), " .
+ "v2 varchar(30)) " .
+ "engine = innodb default charset = binary");
+srand(999);
+
+my %valmap = ();
+
+print "HSINSERT";
+my $hs = hstest::get_hs_connection(undef, 9999);
+my $dbname = $hstest::conf{dbname};
+$hs->open_index(1, $dbname, $table, '', 'k,v1,v2');
+for (my $i = 0; $i < $tablesize; ++$i) {
+ my $k = "" . $i;
+ my $v1 = "v1_" . $i;
+ my $v2 = "v2_" . $i;
+ my $r = $hs->execute_insert(1, [ $k, $v1, $v2 ]);
+ my $err = $r->[0];
+ if ($err != 0) {
+ my $err_str = $r->[1];
+ print "$err $err_str\n";
+ }
+}
+undef $hs;
+
+dump_table();
+
+print "HSUPDATE";
+$hs = hstest::get_hs_connection(undef, 9999);
+$dbname = $hstest::conf{dbname};
+$hs->open_index(2, $dbname, $table, '', 'v1');
+for (my $i = 0; $i < $tablesize; ++$i) {
+ my $r = $hs->execute_single(2, '=', [ $i ], 1000, 0, 'U', [ "mod_$i" ]);
+ my $err = $r->[0];
+ if ($err != 0) {
+ my $err_str = $r->[1];
+ print "$err $err_str\n";
+ }
+}
+undef $hs;
+
+dump_table();
+
+print "HSDELETE\n";
+$hs = hstest::get_hs_connection(undef, 9999);
+$dbname = $hstest::conf{dbname};
+$hs->open_index(3, $dbname, $table, '', '');
+for (my $i = 0; $i < $tablesize; $i = $i + 2) {
+ my $r = $hs->execute_single(3, '=', [ $i ], 1000, 0, 'D');
+ my $err = $r->[0];
+ if ($err != 0) {
+ my $err_str = $r->[1];
+ print "$err $err_str\n";
+ }
+}
+undef $hs;
+
+dump_table();
+
+sub dump_table {
+ print "DUMP_TABLE\n";
+ my $aref = $dbh->selectall_arrayref("select k,v1,v2 from $table order by k");
+ for my $row (@$aref) {
+ my ($k, $v1, $v2) = @$row;
+ $v1 = "[null]" if !defined($v1);
+ $v2 = "[null]" if !defined($v2);
+ print "$k $v1 $v2\n";
+ # print "MISMATCH\n" if ($valmap{$k} ne $v);
+ }
+}
+
diff --git a/plugin/handler_socket/regtest/test_01_lib/test07.expected b/plugin/handler_socket/regtest/test_01_lib/test07.expected
new file mode 100644
index 00000000000..f2047c2e237
--- /dev/null
+++ b/plugin/handler_socket/regtest/test_01_lib/test07.expected
@@ -0,0 +1,304 @@
+MY
+0 1v1020 2v6350
+1 1v8031 2v9251
+2 1v7752 2v5372
+3 [NULL] 2v4143
+4 1v5904 2v3024
+5 1v7045 2v7515
+6 1v3676 2v4006
+7 1v3977 2v1707
+8 1v7198 2v7348
+9 1v5879 2v4949
+10 1v52310 2v95410
+11 1v43311 2v82011
+12 1v28312 2v83712
+13 [NULL] 2v41513
+14 1v54514 2v58314
+15 1v5215 2v32315
+16 1v61416 2v67916
+17 1v80517 2v45117
+18 1v11518 2v26918
+19 1v21819 2v61719
+20 1v87820 2v34520
+21 1v51221 2v96921
+22 1v40822 2v29122
+23 [NULL] 2v95323
+24 1v71024 2v14224
+25 1v68225 2v93425
+26 1v62126 2v96526
+27 1v57427 2v20427
+28 1v29828 2v13428
+29 1v98329 2v44429
+30 1v14430 2v15230
+31 1v18731 2v21531
+32 1v832 2v69732
+33 [NULL] 2v28033
+34 1v70134 2v53734
+35 1v41335 2v6935
+36 1v8636 2v82236
+37 1v67037 2v37037
+38 1v80638 2v68838
+39 1v2639 2v6639
+40 1v80240 2v17140
+41 1v55741 2v84741
+42 1v77742 2v73042
+43 [NULL] 2v11543
+44 1v64644 2v49644
+45 1v12045 2v68445
+46 1v37446 2v6546
+47 1v37047 2v17447
+48 1v82848 2v86748
+49 1v75949 2v70349
+50 1v84350 2v94250
+51 1v40151 2v36251
+52 1v36752 2v30752
+53 [NULL] 2v16753
+54 1v79954 2v82954
+55 1v53955 2v37955
+56 1v56056 2v85856
+57 1v57957 2v2657
+58 1v88758 2v50758
+59 1v34559 2v89859
+60 1v43060 2v80160
+61 1v84561 2v32561
+62 1v62462 2v48062
+63 [NULL] 2v67663
+64 1v5364 2v73664
+65 1v80965 2v27065
+66 1v17566 2v83966
+67 1v6167 2v22267
+68 1v35868 2v50568
+69 1v62769 2v90669
+70 1v4670 2v98170
+71 1v11971 2v471
+72 1v1472 2v48072
+73 [NULL] 2v40573
+74 1v87574 2v63974
+75 1v82775 2v34475
+76 1v59876 2v56376
+77 1v77077 2v51677
+78 1v53878 2v54878
+79 1v35779 2v32279
+80 1v3680 2v37080
+81 1v33181 2v81581
+82 1v76982 2v66882
+83 [NULL] 2v28183
+84 1v69684 2v45284
+85 1v40685 2v185
+86 1v39586 2v32486
+87 1v3687 2v73887
+88 1v16088 2v7988
+89 1v75989 2v65789
+90 1v3190 2v78390
+91 1v65091 2v82491
+92 1v85292 2v6892
+93 [NULL] 2v54693
+94 1v81394 2v77594
+95 1v8795 2v69695
+96 1v19696 2v38096
+97 1v7997 2v75197
+98 1v32398 2v21798
+99 1v3799 2v70199
+HS
+0 1v1020 2v6350
+1 1v8031 2v9251
+2 1v7752 2v5372
+3 [NULL] 2v4143
+4 1v5904 2v3024
+5 1v7045 2v7515
+6 1v3676 2v4006
+7 1v3977 2v1707
+8 1v7198 2v7348
+9 1v5879 2v4949
+10 1v52310 2v95410
+11 1v43311 2v82011
+12 1v28312 2v83712
+13 [NULL] 2v41513
+14 1v54514 2v58314
+15 1v5215 2v32315
+16 1v61416 2v67916
+17 1v80517 2v45117
+18 1v11518 2v26918
+19 1v21819 2v61719
+20 1v87820 2v34520
+21 1v51221 2v96921
+22 1v40822 2v29122
+23 [NULL] 2v95323
+24 1v71024 2v14224
+25 1v68225 2v93425
+26 1v62126 2v96526
+27 1v57427 2v20427
+28 1v29828 2v13428
+29 1v98329 2v44429
+30 1v14430 2v15230
+31 1v18731 2v21531
+32 1v832 2v69732
+33 [NULL] 2v28033
+34 1v70134 2v53734
+35 1v41335 2v6935
+36 1v8636 2v82236
+37 1v67037 2v37037
+38 1v80638 2v68838
+39 1v2639 2v6639
+40 1v80240 2v17140
+41 1v55741 2v84741
+42 1v77742 2v73042
+43 [NULL] 2v11543
+44 1v64644 2v49644
+45 1v12045 2v68445
+46 1v37446 2v6546
+47 1v37047 2v17447
+48 1v82848 2v86748
+49 1v75949 2v70349
+50 1v84350 2v94250
+51 1v40151 2v36251
+52 1v36752 2v30752
+53 [NULL] 2v16753
+54 1v79954 2v82954
+55 1v53955 2v37955
+56 1v56056 2v85856
+57 1v57957 2v2657
+58 1v88758 2v50758
+59 1v34559 2v89859
+60 1v43060 2v80160
+61 1v84561 2v32561
+62 1v62462 2v48062
+63 [NULL] 2v67663
+64 1v5364 2v73664
+65 1v80965 2v27065
+66 1v17566 2v83966
+67 1v6167 2v22267
+68 1v35868 2v50568
+69 1v62769 2v90669
+70 1v4670 2v98170
+71 1v11971 2v471
+72 1v1472 2v48072
+73 [NULL] 2v40573
+74 1v87574 2v63974
+75 1v82775 2v34475
+76 1v59876 2v56376
+77 1v77077 2v51677
+78 1v53878 2v54878
+79 1v35779 2v32279
+80 1v3680 2v37080
+81 1v33181 2v81581
+82 1v76982 2v66882
+83 [NULL] 2v28183
+84 1v69684 2v45284
+85 1v40685 2v185
+86 1v39586 2v32486
+87 1v3687 2v73887
+88 1v16088 2v7988
+89 1v75989 2v65789
+90 1v3190 2v78390
+91 1v65091 2v82491
+92 1v85292 2v6892
+93 [NULL] 2v54693
+94 1v81394 2v77594
+95 1v8795 2v69695
+96 1v19696 2v38096
+97 1v7997 2v75197
+98 1v32398 2v21798
+99 1v3799 2v70199
+2ndIDX
+2ndidx 0 1v1020 => 0 1v1020 2v6350
+2ndidx 1 1v8031 => 1 1v8031 2v9251
+2ndidx 2 1v7752 => 2 1v7752 2v5372
+2ndidx 4 1v5904 => 4 1v5904 2v3024
+2ndidx 5 1v7045 => 5 1v7045 2v7515
+2ndidx 6 1v3676 => 6 1v3676 2v4006
+2ndidx 7 1v3977 => 7 1v3977 2v1707
+2ndidx 8 1v7198 => 8 1v7198 2v7348
+2ndidx 9 1v5879 => 9 1v5879 2v4949
+2ndidx 10 1v52310 => 10 1v52310 2v95410
+2ndidx 11 1v43311 => 11 1v43311 2v82011
+2ndidx 12 1v28312 => 12 1v28312 2v83712
+2ndidx 14 1v54514 => 14 1v54514 2v58314
+2ndidx 15 1v5215 => 15 1v5215 2v32315
+2ndidx 16 1v61416 => 16 1v61416 2v67916
+2ndidx 17 1v80517 => 17 1v80517 2v45117
+2ndidx 18 1v11518 => 18 1v11518 2v26918
+2ndidx 19 1v21819 => 19 1v21819 2v61719
+2ndidx 20 1v87820 => 20 1v87820 2v34520
+2ndidx 21 1v51221 => 21 1v51221 2v96921
+2ndidx 22 1v40822 => 22 1v40822 2v29122
+2ndidx 24 1v71024 => 24 1v71024 2v14224
+2ndidx 25 1v68225 => 25 1v68225 2v93425
+2ndidx 26 1v62126 => 26 1v62126 2v96526
+2ndidx 27 1v57427 => 27 1v57427 2v20427
+2ndidx 28 1v29828 => 28 1v29828 2v13428
+2ndidx 29 1v98329 => 29 1v98329 2v44429
+2ndidx 30 1v14430 => 30 1v14430 2v15230
+2ndidx 31 1v18731 => 31 1v18731 2v21531
+2ndidx 32 1v832 => 32 1v832 2v69732
+2ndidx 34 1v70134 => 34 1v70134 2v53734
+2ndidx 35 1v41335 => 35 1v41335 2v6935
+2ndidx 36 1v8636 => 36 1v8636 2v82236
+2ndidx 37 1v67037 => 37 1v67037 2v37037
+2ndidx 38 1v80638 => 38 1v80638 2v68838
+2ndidx 39 1v2639 => 39 1v2639 2v6639
+2ndidx 40 1v80240 => 40 1v80240 2v17140
+2ndidx 41 1v55741 => 41 1v55741 2v84741
+2ndidx 42 1v77742 => 42 1v77742 2v73042
+2ndidx 44 1v64644 => 44 1v64644 2v49644
+2ndidx 45 1v12045 => 45 1v12045 2v68445
+2ndidx 46 1v37446 => 46 1v37446 2v6546
+2ndidx 47 1v37047 => 47 1v37047 2v17447
+2ndidx 48 1v82848 => 48 1v82848 2v86748
+2ndidx 49 1v75949 => 49 1v75949 2v70349
+2ndidx 50 1v84350 => 50 1v84350 2v94250
+2ndidx 51 1v40151 => 51 1v40151 2v36251
+2ndidx 52 1v36752 => 52 1v36752 2v30752
+2ndidx 54 1v79954 => 54 1v79954 2v82954
+2ndidx 55 1v53955 => 55 1v53955 2v37955
+2ndidx 56 1v56056 => 56 1v56056 2v85856
+2ndidx 57 1v57957 => 57 1v57957 2v2657
+2ndidx 58 1v88758 => 58 1v88758 2v50758
+2ndidx 59 1v34559 => 59 1v34559 2v89859
+2ndidx 60 1v43060 => 60 1v43060 2v80160
+2ndidx 61 1v84561 => 61 1v84561 2v32561
+2ndidx 62 1v62462 => 62 1v62462 2v48062
+2ndidx 64 1v5364 => 64 1v5364 2v73664
+2ndidx 65 1v80965 => 65 1v80965 2v27065
+2ndidx 66 1v17566 => 66 1v17566 2v83966
+2ndidx 67 1v6167 => 67 1v6167 2v22267
+2ndidx 68 1v35868 => 68 1v35868 2v50568
+2ndidx 69 1v62769 => 69 1v62769 2v90669
+2ndidx 70 1v4670 => 70 1v4670 2v98170
+2ndidx 71 1v11971 => 71 1v11971 2v471
+2ndidx 72 1v1472 => 72 1v1472 2v48072
+2ndidx 74 1v87574 => 74 1v87574 2v63974
+2ndidx 75 1v82775 => 75 1v82775 2v34475
+2ndidx 76 1v59876 => 76 1v59876 2v56376
+2ndidx 77 1v77077 => 77 1v77077 2v51677
+2ndidx 78 1v53878 => 78 1v53878 2v54878
+2ndidx 79 1v35779 => 79 1v35779 2v32279
+2ndidx 80 1v3680 => 80 1v3680 2v37080
+2ndidx 81 1v33181 => 81 1v33181 2v81581
+2ndidx 82 1v76982 => 82 1v76982 2v66882
+2ndidx 84 1v69684 => 84 1v69684 2v45284
+2ndidx 85 1v40685 => 85 1v40685 2v185
+2ndidx 86 1v39586 => 86 1v39586 2v32486
+2ndidx 87 1v3687 => 87 1v3687 2v73887
+2ndidx 88 1v16088 => 88 1v16088 2v7988
+2ndidx 89 1v75989 => 89 1v75989 2v65789
+2ndidx 90 1v3190 => 90 1v3190 2v78390
+2ndidx 91 1v65091 => 91 1v65091 2v82491
+2ndidx 92 1v85292 => 92 1v85292 2v6892
+2ndidx 94 1v81394 => 94 1v81394 2v77594
+2ndidx 95 1v8795 => 95 1v8795 2v69695
+2ndidx 96 1v19696 => 96 1v19696 2v38096
+2ndidx 97 1v7997 => 97 1v7997 2v75197
+2ndidx 98 1v32398 => 98 1v32398 2v21798
+2ndidx 99 1v3799 => 99 1v3799 2v70199
+2ndIDX NULL
+2ndidxnull 3 2v4143
+2ndidxnull 13 2v41513
+2ndidxnull 23 2v95323
+2ndidxnull 33 2v28033
+2ndidxnull 43 2v11543
+2ndidxnull 53 2v16753
+2ndidxnull 63 2v67663
+2ndidxnull 73 2v40573
+2ndidxnull 83 2v28183
+2ndidxnull 93 2v54693
diff --git a/plugin/handler_socket/regtest/test_01_lib/test07.pl b/plugin/handler_socket/regtest/test_01_lib/test07.pl
new file mode 100644
index 00000000000..fa9802366d8
--- /dev/null
+++ b/plugin/handler_socket/regtest/test_01_lib/test07.pl
@@ -0,0 +1,98 @@
+#!/usr/bin/perl
+
+# vim:sw=2:ai
+
+# test for nulls
+
+BEGIN {
+ push @INC, "../common/";
+};
+
+# use strict;
+use warnings;
+use hstest;
+
+my $dbh = hstest::init_testdb();
+my $table = 'hstesttbl';
+my $tablesize = 100;
+$dbh->do(
+ "create table $table (" .
+ "k int primary key, v1 varchar(30), v2 varchar(30), " .
+ "key idxv1 (v1) " .
+ ") engine = innodb");
+srand(999);
+
+my %valmap = ();
+
+my $sth = $dbh->prepare("insert into $table values (?,?,?)");
+for (my $i = 0; $i < $tablesize; ++$i) {
+ my $k = "" . $i;
+ my $v1 = "1v" . int(rand(1000)) . $i;
+ my $v2 = "2v" . int(rand(1000)) . $i;
+ if ($i % 10 == 3) {
+ $v1 = undef;
+ }
+ $sth->execute($k, $v1, $v2);
+ $valmap{$k} = $v1;
+}
+
+print "MY\n";
+my $aref = $dbh->selectall_arrayref("select k,v1,v2 from $table order by k");
+for my $row (@$aref) {
+ my ($k, $v1, $v2) = @$row;
+ $v1 = "[NULL]" if (!defined($v1));
+ print "$k $v1 $v2\n";
+}
+
+print "HS\n";
+my $hs = hstest::get_hs_connection();
+my $dbname = $hstest::conf{dbname};
+$hs->open_index(1, $dbname, $table, '', 'k,v1,v2');
+my $r = $hs->execute_single(1, '>=', [ '' ], 10000, 0);
+shift(@$r);
+for (my $i = 0; $i < $tablesize; ++$i) {
+ my $k = $r->[$i * 3];
+ my $v1 = $r->[$i * 3 + 1];
+ my $v2 = $r->[$i * 3 + 2];
+ $v1 = "[NULL]" if (!defined($v1));
+ print "$k $v1 $v2\n";
+}
+
+print "2ndIDX\n";
+$hs->open_index(2, $dbname, $table, 'idxv1', 'k,v1,v2');
+for (my $i = 0; $i < $tablesize; ++$i) {
+ my $k = "" . $i;
+ my $v1 = $valmap{$k};
+ next if !defined($v1);
+ my $r = $hs->execute_single(2, '=', [ $v1 ], 1, 0);
+ shift(@$r);
+ my $r_k = $r->[0];
+ my $r_v1 = $r->[1];
+ my $r_v2 = $r->[2];
+ print "2ndidx $k $v1 => $r_k $r_v1 $r_v2\n";
+}
+
+print "2ndIDX NULL\n";
+{
+ my %rvals = ();
+ my $v1 = undef;
+ my @arr;
+ push(@arr, undef);
+ my $kv = \@arr;
+ my $r = $hs->execute_single(2, "=", $kv, 10000, 0);
+ shift(@$r);
+ for (my $i = 0; $i < scalar(@$r); $i += 3) {
+ my $k = $r->[$i];
+ my $v1 = $r->[$i + 1];
+ my $v2 = $r->[$i + 2];
+ $rvals{$k} = [ $k, $v1, $v2 ];
+ }
+ for my $i (sort { $a <=> $b } keys %rvals) {
+ my $rec = $rvals{$i};
+ my $k = $rec->[0];
+ my $v1 = $rec->[1];
+ my $v2 = $rec->[2];
+ print "2ndidxnull $k $v2\n";
+ }
+}
+
diff --git a/plugin/handler_socket/regtest/test_01_lib/test08.expected b/plugin/handler_socket/regtest/test_01_lib/test08.expected
new file mode 100644
index 00000000000..64d705daab8
--- /dev/null
+++ b/plugin/handler_socket/regtest/test_01_lib/test08.expected
@@ -0,0 +1,2 @@
+[0][k5][v5375]
+[0]
diff --git a/plugin/handler_socket/regtest/test_01_lib/test08.pl b/plugin/handler_socket/regtest/test_01_lib/test08.pl
new file mode 100644
index 00000000000..c33bf190d29
--- /dev/null
+++ b/plugin/handler_socket/regtest/test_01_lib/test08.pl
@@ -0,0 +1,48 @@
+#!/usr/bin/perl
+
+# vim:sw=2:ai
+
+# test for not-found
+
+BEGIN {
+ push @INC, "../common/";
+};
+
+use strict;
+use warnings;
+use hstest;
+
+my $dbh = hstest::init_testdb();
+my $table = 'hstesttbl';
+my $tablesize = 100;
+$dbh->do(
+ "create table $table (k varchar(30) primary key, v varchar(30) not null) " .
+ "engine = innodb");
+srand(999);
+
+my %valmap = ();
+
+my $sth = $dbh->prepare("insert into $table values (?,?)");
+for (my $i = 0; $i < $tablesize; ++$i) {
+ my $k = "k" . $i;
+ my $v = "v" . int(rand(1000)) . $i;
+ $sth->execute($k, $v);
+ $valmap{$k} = $v;
+}
+
+my $hs = hstest::get_hs_connection();
+my $dbname = $hstest::conf{dbname};
+$hs->open_index(1, $dbname, $table, '', 'k,v');
+
+dump_rec($hs, 1, 'k5'); # found
+dump_rec($hs, 1, 'k000000'); # notfound
+
+sub dump_rec {
+ my ($hs, $idxid, $key) = @_;
+ my $r = $hs->execute_single($idxid, '=', [ $key ], 1, 0);
+ for my $fld (@$r) {
+ print "[$fld]";
+ }
+ print "\n";
+}
+
diff --git a/plugin/handler_socket/regtest/test_01_lib/test09.expected b/plugin/handler_socket/regtest/test_01_lib/test09.expected
new file mode 100644
index 00000000000..12cb11824db
--- /dev/null
+++ b/plugin/handler_socket/regtest/test_01_lib/test09.expected
@@ -0,0 +1,12 @@
+DEL
+[0][1]
+[0][k50][v68250][k51][v93451]
+DELINS
+[0][k6][v5926][k60][v14460][k61][v15261]
+[0][1]
+[0]
+[0][k6][v5926][k60][INS][k61][v15261]
+DELUPUP
+[0][k7][v4147][k70][v41370][k71][v6971]
+[0][1]
+[0][k7][v4147][k70][UP][k71][v6971]
diff --git a/plugin/handler_socket/regtest/test_01_lib/test09.pl b/plugin/handler_socket/regtest/test_01_lib/test09.pl
new file mode 100644
index 00000000000..14fd9c26641
--- /dev/null
+++ b/plugin/handler_socket/regtest/test_01_lib/test09.pl
@@ -0,0 +1,67 @@
+#!/usr/bin/perl
+
+# vim:sw=2:ai
+
+# test for multiple modify requests
+
+BEGIN {
+ push @INC, "../common/";
+};
+
+use strict;
+use warnings;
+use hstest;
+
+my $dbh = hstest::init_testdb();
+my $table = 'hstesttbl';
+my $tablesize = 100;
+$dbh->do(
+ "create table $table (k varchar(30) primary key, v varchar(30) not null) " .
+ "engine = innodb");
+srand(999);
+
+my %valmap = ();
+
+my $sth = $dbh->prepare("insert into $table values (?,?)");
+for (my $i = 0; $i < $tablesize; ++$i) {
+ my $k = "k" . $i;
+ my $v = "v" . int(rand(1000)) . $i;
+ $sth->execute($k, $v);
+ $valmap{$k} = $v;
+}
+
+my $hs = hstest::get_hs_connection(undef, 9999);
+my $dbname = $hstest::conf{dbname};
+$hs->open_index(1, $dbname, $table, '', 'k,v');
+
+exec_multi(
+ "DEL",
+ [ 1, '=', [ 'k5' ], 1, 0, 'D' ],
+ [ 1, '>=', [ 'k5' ], 2, 0 ],
+);
+exec_multi(
+ "DELINS",
+ [ 1, '>=', [ 'k6' ], 3, 0 ],
+ [ 1, '=', [ 'k60' ], 1, 0, 'D' ],
+ [ 1, '+', [ 'k60', 'INS' ] ],
+ [ 1, '>=', [ 'k6' ], 3, 0 ],
+);
+exec_multi(
+ "DELUPUP",
+ [ 1, '>=', [ 'k7' ], 3, 0 ],
+ [ 1, '=', [ 'k70' ], 1, 0, 'U', [ 'k70', 'UP' ] ],
+ [ 1, '>=', [ 'k7' ], 3, 0 ],
+);
+
+sub exec_multi {
+ my $mess = shift(@_);
+ print "$mess\n";
+ my $mres = $hs->execute_multi(\@_);
+ for my $res (@$mres) {
+ for my $fld (@$res) {
+ print "[$fld]";
+ }
+ print "\n";
+ }
+}
+
diff --git a/plugin/handler_socket/regtest/test_01_lib/test10.expected b/plugin/handler_socket/regtest/test_01_lib/test10.expected
new file mode 100644
index 00000000000..6716abad1c7
--- /dev/null
+++ b/plugin/handler_socket/regtest/test_01_lib/test10.expected
@@ -0,0 +1,771 @@
+HSINSERTDUMP_TABLE
+0 v1_0 [null]
+1 v1_1 [null]
+10 v1_10 [null]
+100 v1_100 [null]
+101 v1_101 [null]
+102 v1_102 [null]
+103 v1_103 [null]
+104 v1_104 [null]
+105 v1_105 [null]
+106 v1_106 [null]
+107 v1_107 [null]
+108 v1_108 [null]
+109 v1_109 [null]
+11 v1_11 [null]
+110 v1_110 [null]
+111 v1_111 [null]
+112 v1_112 [null]
+113 v1_113 [null]
+114 v1_114 [null]
+115 v1_115 [null]
+116 v1_116 [null]
+117 v1_117 [null]
+118 v1_118 [null]
+119 v1_119 [null]
+12 v1_12 [null]
+120 v1_120 [null]
+121 v1_121 [null]
+122 v1_122 [null]
+123 v1_123 [null]
+124 v1_124 [null]
+125 v1_125 [null]
+126 v1_126 [null]
+127 v1_127 [null]
+128 v1_128 [null]
+129 v1_129 [null]
+13 v1_13 [null]
+130 v1_130 [null]
+131 v1_131 [null]
+132 v1_132 [null]
+133 v1_133 [null]
+134 v1_134 [null]
+135 v1_135 [null]
+136 v1_136 [null]
+137 v1_137 [null]
+138 v1_138 [null]
+139 v1_139 [null]
+14 v1_14 [null]
+140 v1_140 [null]
+141 v1_141 [null]
+142 v1_142 [null]
+143 v1_143 [null]
+144 v1_144 [null]
+145 v1_145 [null]
+146 v1_146 [null]
+147 v1_147 [null]
+148 v1_148 [null]
+149 v1_149 [null]
+15 v1_15 [null]
+150 v1_150 [null]
+151 v1_151 [null]
+152 v1_152 [null]
+153 v1_153 [null]
+154 v1_154 [null]
+155 v1_155 [null]
+156 v1_156 [null]
+157 v1_157 [null]
+158 v1_158 [null]
+159 v1_159 [null]
+16 v1_16 [null]
+160 v1_160 [null]
+161 v1_161 [null]
+162 v1_162 [null]
+163 v1_163 [null]
+164 v1_164 [null]
+165 v1_165 [null]
+166 v1_166 [null]
+167 v1_167 [null]
+168 v1_168 [null]
+169 v1_169 [null]
+17 v1_17 [null]
+170 v1_170 [null]
+171 v1_171 [null]
+172 v1_172 [null]
+173 v1_173 [null]
+174 v1_174 [null]
+175 v1_175 [null]
+176 v1_176 [null]
+177 v1_177 [null]
+178 v1_178 [null]
+179 v1_179 [null]
+18 v1_18 [null]
+180 v1_180 [null]
+181 v1_181 [null]
+182 v1_182 [null]
+183 v1_183 [null]
+184 v1_184 [null]
+185 v1_185 [null]
+186 v1_186 [null]
+187 v1_187 [null]
+188 v1_188 [null]
+189 v1_189 [null]
+19 v1_19 [null]
+190 v1_190 [null]
+191 v1_191 [null]
+192 v1_192 [null]
+193 v1_193 [null]
+194 v1_194 [null]
+195 v1_195 [null]
+196 v1_196 [null]
+197 v1_197 [null]
+198 v1_198 [null]
+199 v1_199 [null]
+2 v1_2 [null]
+20 v1_20 [null]
+200 v1_200 [null]
+201 v1_201 [null]
+202 v1_202 [null]
+203 v1_203 [null]
+204 v1_204 [null]
+205 v1_205 [null]
+206 v1_206 [null]
+207 v1_207 [null]
+208 v1_208 [null]
+209 v1_209 [null]
+21 v1_21 [null]
+210 v1_210 [null]
+211 v1_211 [null]
+212 v1_212 [null]
+213 v1_213 [null]
+214 v1_214 [null]
+215 v1_215 [null]
+216 v1_216 [null]
+217 v1_217 [null]
+218 v1_218 [null]
+219 v1_219 [null]
+22 v1_22 [null]
+220 v1_220 [null]
+221 v1_221 [null]
+222 v1_222 [null]
+223 v1_223 [null]
+224 v1_224 [null]
+225 v1_225 [null]
+226 v1_226 [null]
+227 v1_227 [null]
+228 v1_228 [null]
+229 v1_229 [null]
+23 v1_23 [null]
+230 v1_230 [null]
+231 v1_231 [null]
+232 v1_232 [null]
+233 v1_233 [null]
+234 v1_234 [null]
+235 v1_235 [null]
+236 v1_236 [null]
+237 v1_237 [null]
+238 v1_238 [null]
+239 v1_239 [null]
+24 v1_24 [null]
+240 v1_240 [null]
+241 v1_241 [null]
+242 v1_242 [null]
+243 v1_243 [null]
+244 v1_244 [null]
+245 v1_245 [null]
+246 v1_246 [null]
+247 v1_247 [null]
+248 v1_248 [null]
+249 v1_249 [null]
+25 v1_25 [null]
+250 v1_250 [null]
+251 v1_251 [null]
+252 v1_252 [null]
+253 v1_253 [null]
+254 v1_254 [null]
+255 v1_255 [null]
+26 v1_26 [null]
+27 v1_27 [null]
+28 v1_28 [null]
+29 v1_29 [null]
+3 v1_3 [null]
+30 v1_30 [null]
+31 v1_31 [null]
+32 v1_32 [null]
+33 v1_33 [null]
+34 v1_34 [null]
+35 v1_35 [null]
+36 v1_36 [null]
+37 v1_37 [null]
+38 v1_38 [null]
+39 v1_39 [null]
+4 v1_4 [null]
+40 v1_40 [null]
+41 v1_41 [null]
+42 v1_42 [null]
+43 v1_43 [null]
+44 v1_44 [null]
+45 v1_45 [null]
+46 v1_46 [null]
+47 v1_47 [null]
+48 v1_48 [null]
+49 v1_49 [null]
+5 v1_5 [null]
+50 v1_50 [null]
+51 v1_51 [null]
+52 v1_52 [null]
+53 v1_53 [null]
+54 v1_54 [null]
+55 v1_55 [null]
+56 v1_56 [null]
+57 v1_57 [null]
+58 v1_58 [null]
+59 v1_59 [null]
+6 v1_6 [null]
+60 v1_60 [null]
+61 v1_61 [null]
+62 v1_62 [null]
+63 v1_63 [null]
+64 v1_64 [null]
+65 v1_65 [null]
+66 v1_66 [null]
+67 v1_67 [null]
+68 v1_68 [null]
+69 v1_69 [null]
+7 v1_7 [null]
+70 v1_70 [null]
+71 v1_71 [null]
+72 v1_72 [null]
+73 v1_73 [null]
+74 v1_74 [null]
+75 v1_75 [null]
+76 v1_76 [null]
+77 v1_77 [null]
+78 v1_78 [null]
+79 v1_79 [null]
+8 v1_8 [null]
+80 v1_80 [null]
+81 v1_81 [null]
+82 v1_82 [null]
+83 v1_83 [null]
+84 v1_84 [null]
+85 v1_85 [null]
+86 v1_86 [null]
+87 v1_87 [null]
+88 v1_88 [null]
+89 v1_89 [null]
+9 v1_9 [null]
+90 v1_90 [null]
+91 v1_91 [null]
+92 v1_92 [null]
+93 v1_93 [null]
+94 v1_94 [null]
+95 v1_95 [null]
+96 v1_96 [null]
+97 v1_97 [null]
+98 v1_98 [null]
+99 v1_99 [null]
+HSUPDATEDUMP_TABLE
+0 [null] [null]
+1 [null] [null]
+10 [null] [null]
+100 [null] [null]
+101 [null] [null]
+102 [null] [null]
+103 [null] [null]
+104 [null] [null]
+105 [null] [null]
+106 [null] [null]
+107 [null] [null]
+108 [null] [null]
+109 [null] [null]
+11 [null] [null]
+110 [null] [null]
+111 [null] [null]
+112 [null] [null]
+113 [null] [null]
+114 [null] [null]
+115 [null] [null]
+116 [null] [null]
+117 [null] [null]
+118 [null] [null]
+119 [null] [null]
+12 [null] [null]
+120 [null] [null]
+121 [null] [null]
+122 [null] [null]
+123 [null] [null]
+124 [null] [null]
+125 [null] [null]
+126 [null] [null]
+127 [null] [null]
+128 [null] [null]
+129 [null] [null]
+13 [null] [null]
+130 [null] [null]
+131 [null] [null]
+132 [null] [null]
+133 [null] [null]
+134 [null] [null]
+135 [null] [null]
+136 [null] [null]
+137 [null] [null]
+138 [null] [null]
+139 [null] [null]
+14 [null] [null]
+140 [null] [null]
+141 [null] [null]
+142 [null] [null]
+143 [null] [null]
+144 [null] [null]
+145 [null] [null]
+146 [null] [null]
+147 [null] [null]
+148 [null] [null]
+149 [null] [null]
+15 [null] [null]
+150 [null] [null]
+151 [null] [null]
+152 [null] [null]
+153 [null] [null]
+154 [null] [null]
+155 [null] [null]
+156 [null] [null]
+157 [null] [null]
+158 [null] [null]
+159 [null] [null]
+16 [null] [null]
+160 [null] [null]
+161 [null] [null]
+162 [null] [null]
+163 [null] [null]
+164 [null] [null]
+165 [null] [null]
+166 [null] [null]
+167 [null] [null]
+168 [null] [null]
+169 [null] [null]
+17 [null] [null]
+170 [null] [null]
+171 [null] [null]
+172 [null] [null]
+173 [null] [null]
+174 [null] [null]
+175 [null] [null]
+176 [null] [null]
+177 [null] [null]
+178 [null] [null]
+179 [null] [null]
+18 [null] [null]
+180 [null] [null]
+181 [null] [null]
+182 [null] [null]
+183 [null] [null]
+184 [null] [null]
+185 [null] [null]
+186 [null] [null]
+187 [null] [null]
+188 [null] [null]
+189 [null] [null]
+19 [null] [null]
+190 [null] [null]
+191 [null] [null]
+192 [null] [null]
+193 [null] [null]
+194 [null] [null]
+195 [null] [null]
+196 [null] [null]
+197 [null] [null]
+198 [null] [null]
+199 [null] [null]
+2 [null] [null]
+20 [null] [null]
+200 [null] [null]
+201 [null] [null]
+202 [null] [null]
+203 [null] [null]
+204 [null] [null]
+205 [null] [null]
+206 [null] [null]
+207 [null] [null]
+208 [null] [null]
+209 [null] [null]
+21 [null] [null]
+210 [null] [null]
+211 [null] [null]
+212 [null] [null]
+213 [null] [null]
+214 [null] [null]
+215 [null] [null]
+216 [null] [null]
+217 [null] [null]
+218 [null] [null]
+219 [null] [null]
+22 [null] [null]
+220 [null] [null]
+221 [null] [null]
+222 [null] [null]
+223 [null] [null]
+224 [null] [null]
+225 [null] [null]
+226 [null] [null]
+227 [null] [null]
+228 [null] [null]
+229 [null] [null]
+23 [null] [null]
+230 [null] [null]
+231 [null] [null]
+232 [null] [null]
+233 [null] [null]
+234 [null] [null]
+235 [null] [null]
+236 [null] [null]
+237 [null] [null]
+238 [null] [null]
+239 [null] [null]
+24 [null] [null]
+240 [null] [null]
+241 [null] [null]
+242 [null] [null]
+243 [null] [null]
+244 [null] [null]
+245 [null] [null]
+246 [null] [null]
+247 [null] [null]
+248 [null] [null]
+249 [null] [null]
+25 [null] [null]
+250 [null] [null]
+251 [null] [null]
+252 [null] [null]
+253 [null] [null]
+254 [null] [null]
+255 [null] [null]
+26 [null] [null]
+27 [null] [null]
+28 [null] [null]
+29 [null] [null]
+3 [null] [null]
+30 [null] [null]
+31 [null] [null]
+32 [null] [null]
+33 [null] [null]
+34 [null] [null]
+35 [null] [null]
+36 [null] [null]
+37 [null] [null]
+38 [null] [null]
+39 [null] [null]
+4 [null] [null]
+40 [null] [null]
+41 [null] [null]
+42 [null] [null]
+43 [null] [null]
+44 [null] [null]
+45 [null] [null]
+46 [null] [null]
+47 [null] [null]
+48 [null] [null]
+49 [null] [null]
+5 [null] [null]
+50 [null] [null]
+51 [null] [null]
+52 [null] [null]
+53 [null] [null]
+54 [null] [null]
+55 [null] [null]
+56 [null] [null]
+57 [null] [null]
+58 [null] [null]
+59 [null] [null]
+6 [null] [null]
+60 [null] [null]
+61 [null] [null]
+62 [null] [null]
+63 [null] [null]
+64 [null] [null]
+65 [null] [null]
+66 [null] [null]
+67 [null] [null]
+68 [null] [null]
+69 [null] [null]
+7 [null] [null]
+70 [null] [null]
+71 [null] [null]
+72 [null] [null]
+73 [null] [null]
+74 [null] [null]
+75 [null] [null]
+76 [null] [null]
+77 [null] [null]
+78 [null] [null]
+79 [null] [null]
+8 [null] [null]
+80 [null] [null]
+81 [null] [null]
+82 [null] [null]
+83 [null] [null]
+84 [null] [null]
+85 [null] [null]
+86 [null] [null]
+87 [null] [null]
+88 [null] [null]
+89 [null] [null]
+9 [null] [null]
+90 [null] [null]
+91 [null] [null]
+92 [null] [null]
+93 [null] [null]
+94 [null] [null]
+95 [null] [null]
+96 [null] [null]
+97 [null] [null]
+98 [null] [null]
+99 [null] [null]
+HSUPDATEDUMP_TABLE
+0 hoge [null]
+1 hoge [null]
+10 hoge [null]
+100 hoge [null]
+101 hoge [null]
+102 hoge [null]
+103 hoge [null]
+104 hoge [null]
+105 hoge [null]
+106 hoge [null]
+107 hoge [null]
+108 hoge [null]
+109 hoge [null]
+11 hoge [null]
+110 hoge [null]
+111 hoge [null]
+112 hoge [null]
+113 hoge [null]
+114 hoge [null]
+115 hoge [null]
+116 hoge [null]
+117 hoge [null]
+118 hoge [null]
+119 hoge [null]
+12 hoge [null]
+120 hoge [null]
+121 hoge [null]
+122 hoge [null]
+123 hoge [null]
+124 hoge [null]
+125 hoge [null]
+126 hoge [null]
+127 hoge [null]
+128 hoge [null]
+129 hoge [null]
+13 hoge [null]
+130 hoge [null]
+131 hoge [null]
+132 hoge [null]
+133 hoge [null]
+134 hoge [null]
+135 hoge [null]
+136 hoge [null]
+137 hoge [null]
+138 hoge [null]
+139 hoge [null]
+14 hoge [null]
+140 hoge [null]
+141 hoge [null]
+142 hoge [null]
+143 hoge [null]
+144 hoge [null]
+145 hoge [null]
+146 hoge [null]
+147 hoge [null]
+148 hoge [null]
+149 hoge [null]
+15 hoge [null]
+150 hoge [null]
+151 hoge [null]
+152 hoge [null]
+153 hoge [null]
+154 hoge [null]
+155 hoge [null]
+156 hoge [null]
+157 hoge [null]
+158 hoge [null]
+159 hoge [null]
+16 hoge [null]
+160 hoge [null]
+161 hoge [null]
+162 hoge [null]
+163 hoge [null]
+164 hoge [null]
+165 hoge [null]
+166 hoge [null]
+167 hoge [null]
+168 hoge [null]
+169 hoge [null]
+17 hoge [null]
+170 hoge [null]
+171 hoge [null]
+172 hoge [null]
+173 hoge [null]
+174 hoge [null]
+175 hoge [null]
+176 hoge [null]
+177 hoge [null]
+178 hoge [null]
+179 hoge [null]
+18 hoge [null]
+180 hoge [null]
+181 hoge [null]
+182 hoge [null]
+183 hoge [null]
+184 hoge [null]
+185 hoge [null]
+186 hoge [null]
+187 hoge [null]
+188 hoge [null]
+189 hoge [null]
+19 hoge [null]
+190 hoge [null]
+191 hoge [null]
+192 hoge [null]
+193 hoge [null]
+194 hoge [null]
+195 hoge [null]
+196 hoge [null]
+197 hoge [null]
+198 hoge [null]
+199 hoge [null]
+2 hoge [null]
+20 hoge [null]
+200 hoge [null]
+201 hoge [null]
+202 hoge [null]
+203 hoge [null]
+204 hoge [null]
+205 hoge [null]
+206 hoge [null]
+207 hoge [null]
+208 hoge [null]
+209 hoge [null]
+21 hoge [null]
+210 hoge [null]
+211 hoge [null]
+212 hoge [null]
+213 hoge [null]
+214 hoge [null]
+215 hoge [null]
+216 hoge [null]
+217 hoge [null]
+218 hoge [null]
+219 hoge [null]
+22 hoge [null]
+220 hoge [null]
+221 hoge [null]
+222 hoge [null]
+223 hoge [null]
+224 hoge [null]
+225 hoge [null]
+226 hoge [null]
+227 hoge [null]
+228 hoge [null]
+229 hoge [null]
+23 hoge [null]
+230 hoge [null]
+231 hoge [null]
+232 hoge [null]
+233 hoge [null]
+234 hoge [null]
+235 hoge [null]
+236 hoge [null]
+237 hoge [null]
+238 hoge [null]
+239 hoge [null]
+24 hoge [null]
+240 hoge [null]
+241 hoge [null]
+242 hoge [null]
+243 hoge [null]
+244 hoge [null]
+245 hoge [null]
+246 hoge [null]
+247 hoge [null]
+248 hoge [null]
+249 hoge [null]
+25 hoge [null]
+250 hoge [null]
+251 hoge [null]
+252 hoge [null]
+253 hoge [null]
+254 hoge [null]
+255 hoge [null]
+26 hoge [null]
+27 hoge [null]
+28 hoge [null]
+29 hoge [null]
+3 hoge [null]
+30 hoge [null]
+31 hoge [null]
+32 hoge [null]
+33 hoge [null]
+34 hoge [null]
+35 hoge [null]
+36 hoge [null]
+37 hoge [null]
+38 hoge [null]
+39 hoge [null]
+4 hoge [null]
+40 hoge [null]
+41 hoge [null]
+42 hoge [null]
+43 hoge [null]
+44 hoge [null]
+45 hoge [null]
+46 hoge [null]
+47 hoge [null]
+48 hoge [null]
+49 hoge [null]
+5 hoge [null]
+50 hoge [null]
+51 hoge [null]
+52 hoge [null]
+53 hoge [null]
+54 hoge [null]
+55 hoge [null]
+56 hoge [null]
+57 hoge [null]
+58 hoge [null]
+59 hoge [null]
+6 hoge [null]
+60 hoge [null]
+61 hoge [null]
+62 hoge [null]
+63 hoge [null]
+64 hoge [null]
+65 hoge [null]
+66 hoge [null]
+67 hoge [null]
+68 hoge [null]
+69 hoge [null]
+7 hoge [null]
+70 hoge [null]
+71 hoge [null]
+72 hoge [null]
+73 hoge [null]
+74 hoge [null]
+75 hoge [null]
+76 hoge [null]
+77 hoge [null]
+78 hoge [null]
+79 hoge [null]
+8 hoge [null]
+80 hoge [null]
+81 hoge [null]
+82 hoge [null]
+83 hoge [null]
+84 hoge [null]
+85 hoge [null]
+86 hoge [null]
+87 hoge [null]
+88 hoge [null]
+89 hoge [null]
+9 hoge [null]
+90 hoge [null]
+91 hoge [null]
+92 hoge [null]
+93 hoge [null]
+94 hoge [null]
+95 hoge [null]
+96 hoge [null]
+97 hoge [null]
+98 hoge [null]
+99 hoge [null]
diff --git a/plugin/handler_socket/regtest/test_01_lib/test10.pl b/plugin/handler_socket/regtest/test_01_lib/test10.pl
new file mode 100644
index 00000000000..fd294fe8b78
--- /dev/null
+++ b/plugin/handler_socket/regtest/test_01_lib/test10.pl
@@ -0,0 +1,93 @@
+#!/usr/bin/perl
+
+# vim:sw=2:ai
+
+# test for nulls
+
+BEGIN {
+ push @INC, "../common/";
+};
+
+use strict;
+use warnings;
+use hstest;
+
+my $dbh = hstest::init_testdb();
+my $table = 'hstesttbl';
+my $tablesize = 256;
+$dbh->do(
+ "create table $table (" .
+ "k varchar(30) primary key, " .
+ "v1 varchar(30), " .
+ "v2 varchar(30)) " .
+ "engine = innodb default charset = binary");
+srand(999);
+
+my %valmap = ();
+
+# setting null
+print "HSINSERT";
+my $hs = hstest::get_hs_connection(undef, 9999);
+my $dbname = $hstest::conf{dbname};
+$hs->open_index(1, $dbname, $table, '', 'k,v1,v2');
+for (my $i = 0; $i < $tablesize; ++$i) {
+ my $k = "" . $i;
+ my $v1 = "v1_" . $i;
+ my $v2 = undef; # null value
+ my $r = $hs->execute_insert(1, [ $k, $v1, $v2 ]);
+ my $err = $r->[0];
+ if ($err != 0) {
+ my $err_str = $r->[1];
+ print "$err $err_str\n";
+ }
+}
+undef $hs;
+
+dump_table();
+
+# setting null
+print "HSUPDATE";
+$hs = hstest::get_hs_connection(undef, 9999);
+$dbname = $hstest::conf{dbname};
+$hs->open_index(2, $dbname, $table, '', 'v1');
+for (my $i = 0; $i < $tablesize; ++$i) {
+ my $r = $hs->execute_single(2, '=', [ $i ], 1000, 0, 'U', [ undef ]);
+ my $err = $r->[0];
+ if ($err != 0) {
+ my $err_str = $r->[1];
+ print "$err $err_str\n";
+ }
+}
+undef $hs;
+
+dump_table();
+
+# setting non-null
+print "HSUPDATE";
+$hs = hstest::get_hs_connection(undef, 9999);
+$dbname = $hstest::conf{dbname};
+$hs->open_index(2, $dbname, $table, '', 'v1');
+for (my $i = 0; $i < $tablesize; ++$i) {
+ my $r = $hs->execute_single(2, '=', [ $i ], 1000, 0, 'U', [ "hoge" ]);
+ my $err = $r->[0];
+ if ($err != 0) {
+ my $err_str = $r->[1];
+ print "$err $err_str\n";
+ }
+}
+undef $hs;
+
+dump_table();
+
+sub dump_table {
+ print "DUMP_TABLE\n";
+ my $aref = $dbh->selectall_arrayref("select k,v1,v2 from $table order by k");
+ for my $row (@$aref) {
+ my ($k, $v1, $v2) = @$row;
+ $v1 = "[null]" if !defined($v1);
+ $v2 = "[null]" if !defined($v2);
+ print "$k $v1 $v2\n";
+ # print "MISMATCH\n" if ($valmap{$k} ne $v);
+ }
+}
+
diff --git a/plugin/handler_socket/regtest/test_01_lib/test11.expected b/plugin/handler_socket/regtest/test_01_lib/test11.expected
new file mode 100644
index 00000000000..4359d470cb8
--- /dev/null
+++ b/plugin/handler_socket/regtest/test_01_lib/test11.expected
@@ -0,0 +1,37 @@
+VAL
+[0][k5][5]
+[0][k6][6]
+[0][k7][7]
+[0][k8][8]
+INCREMENT
+[0][1]
+[0][1]
+[0][1]
+[0][1]
+VAL
+[0][k5][8]
+[0][k6][18]
+[0][k7][-4]
+[0][k8][-7]
+DECREMENT
+[0][1]
+[0][0]
+[0][1]
+[0][0]
+VAL
+[0][k5][6]
+[0][k6][18]
+[0][k7][-84]
+[0][k8][-7]
+INCREMENT
+[0][6]
+[0][7]
+[0][8]
+[0][9]
+[0][10]
+[0][11]
+[0][12]
+[0][13]
+[0][14]
+VAL
+[0][k5][15]
diff --git a/plugin/handler_socket/regtest/test_01_lib/test11.pl b/plugin/handler_socket/regtest/test_01_lib/test11.pl
new file mode 100644
index 00000000000..5cfe3e83614
--- /dev/null
+++ b/plugin/handler_socket/regtest/test_01_lib/test11.pl
@@ -0,0 +1,112 @@
+#!/usr/bin/perl
+
+# vim:sw=2:ai
+
+# test for increment/decrement
+
+BEGIN {
+ push @INC, "../common/";
+};
+
+use strict;
+use warnings;
+use hstest;
+
+my $dbh = hstest::init_testdb();
+my $table = 'hstesttbl';
+my $tablesize = 100;
+$dbh->do(
+ "create table $table (k varchar(30) primary key, v varchar(30) not null) " .
+ "engine = innodb");
+srand(999);
+
+my %valmap = ();
+
+my $sth = $dbh->prepare("insert into $table values (?,?)");
+for (my $i = 0; $i < $tablesize; ++$i) {
+ my $k = "k" . $i;
+ my $v = $i;
+ $sth->execute($k, $v);
+ $valmap{$k} = $v;
+}
+
+my $hs = hstest::get_hs_connection(undef, 9999);
+my $dbname = $hstest::conf{dbname};
+$hs->open_index(1, $dbname, $table, '', 'k,v');
+$hs->open_index(2, $dbname, $table, '', 'v');
+
+exec_multi(
+ "VAL",
+ [ 1, '=', [ 'k5' ], 1, 0 ],
+ [ 1, '=', [ 'k6' ], 1, 0 ],
+ [ 1, '=', [ 'k7' ], 1, 0 ],
+ [ 1, '=', [ 'k8' ], 1, 0 ],
+);
+# 5, 6, 7, 8
+
+exec_multi(
+ "INCREMENT",
+ [ 2, '=', [ 'k5' ], 1, 0, '+', [ 3 ] ],
+ [ 2, '=', [ 'k6' ], 1, 0, '+', [ 12 ] ],
+ [ 2, '=', [ 'k7' ], 1, 0, '+', [ -11 ] ],
+ [ 2, '=', [ 'k8' ], 1, 0, '+', [ -15 ] ],
+);
+
+exec_multi(
+ "VAL",
+ [ 1, '=', [ 'k5' ], 1, 0 ],
+ [ 1, '=', [ 'k6' ], 1, 0 ],
+ [ 1, '=', [ 'k7' ], 1, 0 ],
+ [ 1, '=', [ 'k8' ], 1, 0 ],
+);
+# 8, 18, -4, -7
+
+exec_multi(
+ "DECREMENT",
+ [ 2, '=', [ 'k5' ], 1, 0, '-', [ 2 ] ],
+ [ 2, '=', [ 'k6' ], 1, 0, '-', [ 24 ] ],
+ [ 2, '=', [ 'k7' ], 1, 0, '-', [ 80 ] ],
+ [ 2, '=', [ 'k8' ], 1, 0, '-', [ -80 ] ],
+);
+# mod, no, mod, no
+
+exec_multi(
+ "VAL",
+ [ 1, '=', [ 'k5' ], 1, 0 ],
+ [ 1, '=', [ 'k6' ], 1, 0 ],
+ [ 1, '=', [ 'k7' ], 1, 0 ],
+ [ 1, '=', [ 'k8' ], 1, 0 ],
+);
+# 6, 18, -84, -7
+
+exec_multi(
+ "INCREMENT",
+ [ 2, '=', [ 'k5' ], 1, 0, '+?', [ 1 ] ],
+ [ 2, '=', [ 'k5' ], 1, 0, '+?', [ 1 ] ],
+ [ 2, '=', [ 'k5' ], 1, 0, '+?', [ 1 ] ],
+ [ 2, '=', [ 'k5' ], 1, 0, '+?', [ 1 ] ],
+ [ 2, '=', [ 'k5' ], 1, 0, '+?', [ 1 ] ],
+ [ 2, '=', [ 'k5' ], 1, 0, '+?', [ 1 ] ],
+ [ 2, '=', [ 'k5' ], 1, 0, '+?', [ 1 ] ],
+ [ 2, '=', [ 'k5' ], 1, 0, '+?', [ 1 ] ],
+ [ 2, '=', [ 'k5' ], 1, 0, '+?', [ 1 ] ],
+);
+
+exec_multi(
+ "VAL",
+ [ 1, '=', [ 'k5' ], 1, 0 ],
+);
+# 15
+
+sub exec_multi {
+ my $mess = shift(@_);
+ print "$mess\n";
+ my $mres = $hs->execute_multi(\@_);
+ for my $res (@$mres) {
+ for my $fld (@$res) {
+ print "[$fld]";
+ }
+ print "\n";
+ }
+}
+
diff --git a/plugin/handler_socket/regtest/test_01_lib/test12.expected b/plugin/handler_socket/regtest/test_01_lib/test12.expected
new file mode 100644
index 00000000000..96002e798b1
--- /dev/null
+++ b/plugin/handler_socket/regtest/test_01_lib/test12.expected
@@ -0,0 +1,273 @@
+VAL
+code=0
+[k1_0][k2_0][0][0]
+[k1_0][k2_1][1][0]
+[k1_0][k2_2][2][0]
+[k1_0][k2_3][3][0]
+[k1_0][k2_4][4][0]
+[k1_0][k2_5][5][0]
+[k1_0][k2_6][6][0]
+[k1_0][k2_7][7][0]
+[k1_0][k2_8][8][0]
+[k1_0][k2_9][9][0]
+[k1_1][k2_0][1][0]
+[k1_1][k2_1][2][1]
+[k1_1][k2_2][3][2]
+[k1_1][k2_3][4][3]
+[k1_1][k2_4][5][4]
+[k1_1][k2_5][6][5]
+[k1_1][k2_6][7][6]
+[k1_1][k2_7][8][7]
+[k1_1][k2_8][9][8]
+[k1_1][k2_9][10][9]
+[k1_2][k2_0][2][0]
+[k1_2][k2_1][3][2]
+[k1_2][k2_2][4][4]
+[k1_2][k2_3][5][6]
+[k1_2][k2_4][6][8]
+[k1_2][k2_5][7][10]
+[k1_2][k2_6][8][12]
+[k1_2][k2_7][9][14]
+[k1_2][k2_8][10][16]
+[k1_2][k2_9][11][18]
+[k1_3][k2_0][3][0]
+[k1_3][k2_1][4][3]
+[k1_3][k2_2][5][6]
+[k1_3][k2_3][6][9]
+[k1_3][k2_4][7][12]
+[k1_3][k2_5][8][15]
+[k1_3][k2_6][9][18]
+[k1_3][k2_7][10][21]
+[k1_3][k2_8][11][24]
+[k1_3][k2_9][12][27]
+[k1_4][k2_0][4][0]
+[k1_4][k2_1][5][4]
+[k1_4][k2_2][6][8]
+[k1_4][k2_3][7][12]
+[k1_4][k2_4][8][16]
+[k1_4][k2_5][9][20]
+[k1_4][k2_6][10][24]
+[k1_4][k2_7][11][28]
+[k1_4][k2_8][12][32]
+[k1_4][k2_9][13][36]
+[k1_5][k2_0][5][0]
+[k1_5][k2_1][6][5]
+[k1_5][k2_2][7][10]
+[k1_5][k2_3][8][15]
+[k1_5][k2_4][9][20]
+[k1_5][k2_5][10][25]
+[k1_5][k2_6][11][30]
+[k1_5][k2_7][12][35]
+[k1_5][k2_8][13][40]
+[k1_5][k2_9][14][45]
+[k1_6][k2_0][6][0]
+[k1_6][k2_1][7][6]
+[k1_6][k2_2][8][12]
+[k1_6][k2_3][9][18]
+[k1_6][k2_4][10][24]
+[k1_6][k2_5][11][30]
+[k1_6][k2_6][12][36]
+[k1_6][k2_7][13][42]
+[k1_6][k2_8][14][48]
+[k1_6][k2_9][15][54]
+[k1_7][k2_0][7][0]
+[k1_7][k2_1][8][7]
+[k1_7][k2_2][9][14]
+[k1_7][k2_3][10][21]
+[k1_7][k2_4][11][28]
+[k1_7][k2_5][12][35]
+[k1_7][k2_6][13][42]
+[k1_7][k2_7][14][49]
+[k1_7][k2_8][15][56]
+[k1_7][k2_9][16][63]
+[k1_8][k2_0][8][0]
+[k1_8][k2_1][9][8]
+[k1_8][k2_2][10][16]
+[k1_8][k2_3][11][24]
+[k1_8][k2_4][12][32]
+[k1_8][k2_5][13][40]
+[k1_8][k2_6][14][48]
+[k1_8][k2_7][15][56]
+[k1_8][k2_8][16][64]
+[k1_8][k2_9][17][72]
+[k1_9][k2_0][9][0]
+[k1_9][k2_1][10][9]
+[k1_9][k2_2][11][18]
+[k1_9][k2_3][12][27]
+[k1_9][k2_4][13][36]
+[k1_9][k2_5][14][45]
+[k1_9][k2_6][15][54]
+[k1_9][k2_7][16][63]
+[k1_9][k2_8][17][72]
+[k1_9][k2_9][18][81]
+
+FILTER
+code=0
+[k1_0][k2_5][5][0]
+[k1_1][k2_5][6][5]
+[k1_2][k2_5][7][10]
+[k1_3][k2_5][8][15]
+[k1_4][k2_5][9][20]
+[k1_5][k2_5][10][25]
+[k1_6][k2_5][11][30]
+[k1_7][k2_5][12][35]
+[k1_8][k2_5][13][40]
+[k1_9][k2_5][14][45]
+
+FILTER
+code=0
+[k1_0][k2_5][5][0]
+[k1_1][k2_5][6][5]
+[k1_2][k2_5][7][10]
+[k1_3][k2_5][8][15]
+[k1_4][k2_5][9][20]
+[k1_5][k2_5][10][25]
+[k1_6][k2_5][11][30]
+[k1_7][k2_5][12][35]
+[k1_8][k2_5][13][40]
+[k1_9][k2_5][14][45]
+
+FILTER
+code=0
+[k1_0][k2_3][3][0]
+[k1_1][k2_2][3][2]
+[k1_2][k2_1][3][2]
+[k1_3][k2_0][3][0]
+
+FILTER
+code=0
+[k1_1][k2_0][1][0]
+[k1_1][k2_1][2][1]
+[k1_1][k2_2][3][2]
+[k1_1][k2_3][4][3]
+[k1_1][k2_4][5][4]
+[k1_1][k2_5][6][5]
+[k1_1][k2_6][7][6]
+[k1_1][k2_7][8][7]
+[k1_1][k2_8][9][8]
+[k1_1][k2_9][10][9]
+[k1_2][k2_0][2][0]
+[k1_2][k2_1][3][2]
+[k1_2][k2_2][4][4]
+[k1_2][k2_3][5][6]
+[k1_2][k2_4][6][8]
+[k1_2][k2_5][7][10]
+[k1_2][k2_6][8][12]
+[k1_2][k2_7][9][14]
+[k1_2][k2_8][10][16]
+[k1_2][k2_9][11][18]
+
+FILTER
+code=0
+[k1_2][k2_5][7][10]
+[k1_2][k2_6][8][12]
+[k1_2][k2_7][9][14]
+[k1_2][k2_8][10][16]
+[k1_2][k2_9][11][18]
+
+FILTER
+code=0
+[2]
+VAL
+code=0
+[k1_0][k2_0][0][0]
+[k1_0][k2_1][1][0]
+[k1_0][k2_2][2][0]
+[k1_0][k2_3][3][0]
+[k1_0][k2_4][4][0]
+[k1_0][k2_5][5][0]
+[k1_0][k2_6][6][0]
+[k1_0][k2_7][7][0]
+[k1_0][k2_8][8][0]
+[k1_0][k2_9][9][0]
+[k1_1][k2_0][1][0]
+[k1_1][k2_1][2][1]
+[k1_1][k2_2][3][2]
+[k1_1][k2_3][4][3]
+[k1_1][k2_4][5][4]
+[k1_1][k2_5][6][5]
+[k1_1][k2_6][7][6]
+[k1_1][k2_7][8][7]
+[k1_1][k2_8][9][8]
+[k1_1][k2_9][10][9]
+[k1_2][k2_0][2][0]
+[k1_2][k2_1][3][2]
+[k1_2][k2_2][4][4]
+[k1_2][k2_3][5][6]
+[k1_2][k2_4][6][8]
+[k1_2][k2_5][-1][10]
+[k1_2][k2_6][8][12]
+[k1_2][k2_7][9][14]
+[k1_2][k2_8][10][16]
+[k1_2][k2_9][11][18]
+[k1_3][k2_0][3][0]
+[k1_3][k2_1][4][3]
+[k1_3][k2_2][5][6]
+[k1_3][k2_3][6][9]
+[k1_3][k2_4][7][12]
+[k1_3][k2_5][8][15]
+[k1_3][k2_6][9][18]
+[k1_3][k2_7][10][21]
+[k1_3][k2_8][11][24]
+[k1_3][k2_9][12][27]
+[k1_4][k2_0][4][0]
+[k1_4][k2_1][5][4]
+[k1_4][k2_2][6][8]
+[k1_4][k2_3][7][12]
+[k1_4][k2_4][8][16]
+[k1_4][k2_5][9][20]
+[k1_4][k2_6][10][24]
+[k1_4][k2_7][11][28]
+[k1_4][k2_8][12][32]
+[k1_4][k2_9][13][36]
+[k1_5][k2_0][5][0]
+[k1_5][k2_1][6][5]
+[k1_5][k2_2][-1][10]
+[k1_5][k2_3][8][15]
+[k1_5][k2_4][9][20]
+[k1_5][k2_5][10][25]
+[k1_5][k2_6][11][30]
+[k1_5][k2_7][12][35]
+[k1_5][k2_8][13][40]
+[k1_5][k2_9][14][45]
+[k1_6][k2_0][6][0]
+[k1_6][k2_1][7][6]
+[k1_6][k2_2][8][12]
+[k1_6][k2_3][9][18]
+[k1_6][k2_4][10][24]
+[k1_6][k2_5][11][30]
+[k1_6][k2_6][12][36]
+[k1_6][k2_7][13][42]
+[k1_6][k2_8][14][48]
+[k1_6][k2_9][15][54]
+[k1_7][k2_0][7][0]
+[k1_7][k2_1][8][7]
+[k1_7][k2_2][9][14]
+[k1_7][k2_3][10][21]
+[k1_7][k2_4][11][28]
+[k1_7][k2_5][12][35]
+[k1_7][k2_6][13][42]
+[k1_7][k2_7][14][49]
+[k1_7][k2_8][15][56]
+[k1_7][k2_9][16][63]
+[k1_8][k2_0][8][0]
+[k1_8][k2_1][9][8]
+[k1_8][k2_2][10][16]
+[k1_8][k2_3][11][24]
+[k1_8][k2_4][12][32]
+[k1_8][k2_5][13][40]
+[k1_8][k2_6][14][48]
+[k1_8][k2_7][15][56]
+[k1_8][k2_8][16][64]
+[k1_8][k2_9][17][72]
+[k1_9][k2_0][9][0]
+[k1_9][k2_1][10][9]
+[k1_9][k2_2][11][18]
+[k1_9][k2_3][12][27]
+[k1_9][k2_4][13][36]
+[k1_9][k2_5][14][45]
+[k1_9][k2_6][15][54]
+[k1_9][k2_7][16][63]
+[k1_9][k2_8][17][72]
+[k1_9][k2_9][18][81]
+
diff --git a/plugin/handler_socket/regtest/test_01_lib/test12.pl b/plugin/handler_socket/regtest/test_01_lib/test12.pl
new file mode 100644
index 00000000000..100a779de4e
--- /dev/null
+++ b/plugin/handler_socket/regtest/test_01_lib/test12.pl
@@ -0,0 +1,134 @@
+#!/usr/bin/perl
+
+# vim:sw=2:ai
+
+# test for filters
+
+BEGIN {
+ push @INC, "../common/";
+};
+
+use strict;
+use warnings;
+use hstest;
+
+my $dbh = hstest::init_testdb();
+my $table = 'hstesttbl';
+my $tablesize = 10;
+$dbh->do(
+ "create table $table " .
+ "(k1 varchar(30) not null, k2 varchar(30) not null, " .
+ "v1 int not null, v2 int not null, " .
+ "primary key (k1, k2) ) engine = innodb");
+srand(999);
+
+my $sth = $dbh->prepare("insert into $table values (?,?,?,?)");
+for (my $i = 0; $i < $tablesize; ++$i) {
+ for (my $j = 0; $j < $tablesize; ++$j) {
+ my $k1 = "k1_" . $i;
+ my $k2 = "k2_" . $j;
+ my $v1 = $i + $j;
+ my $v2 = $i * $j;
+ $sth->execute($k1, $k2, $v1, $v2);
+ }
+}
+
+my $hs = hstest::get_hs_connection(undef, 9999);
+my $dbname = $hstest::conf{dbname};
+$hs->open_index(1, $dbname, $table, '', 'k1,k2,v1,v2', 'k2');
+$hs->open_index(2, $dbname, $table, '', 'k1,k2,v1,v2', 'k1,k2,v1,v2');
+$hs->open_index(3, $dbname, $table, '', 'v1', 'k1,v2');
+
+exec_multi(
+ 4, "VAL",
+ [ 1, '>=', [ '', '' ], 1000, 0 ],
+);
+# all
+
+# select k1, k2, v1, v2 ... where (k1, k2) >= ('', '') and k2 = 'k2_5'
+exec_single(
+ 4, "FILTER",
+ [ 1, '>=', [ '', '' ], 1000, 0, undef, undef, [ [ 'F', '=', 0, 'k2_5' ] ] ]
+);
+
+# same as above
+exec_multi(
+ 4, "FILTER",
+ [ 1, '>=', [ '', '' ], 1000, 0, undef, undef, [ [ 'F', '=', 0, 'k2_5' ] ] ],
+);
+
+# select k1, k2, v1, v2 ... where (k1, k2) >= ('', '') and v1 = 3
+exec_multi(
+ 4, "FILTER",
+ [ 2, '>=', [ '', '' ], 1000, 0, undef, undef, [ [ 'F', '=', 2, 3 ] ] ],
+);
+
+# select k1, k2, v1, v2 ... where (k1, k2) >= ('k1_1', '') and k1 <= 'k1_2'
+exec_multi(
+ 4, "FILTER",
+ [ 2, '>=', [ 'k1_1', '' ], 1000, 0, undef, undef,
+ [ [ 'W', '<=', 0, 'k1_2' ] ] ],
+);
+
+# select k1, k2, v1, v2 ... where (k1, k2) >= ('k1_1', '') and k1 <= 'k1_2'
+# and v2 >= 10
+exec_multi(
+ 4, "FILTER",
+ [ 2, '>=', [ 'k1_1', '' ], 1000, 0, undef, undef,
+ [ [ 'W', '<=', 0, 'k1_2' ], [ 'F', '>=', 3, 10 ] ] ],
+);
+
+# update ... set v2 = -1 where (k1, k2) >= ('k1_3', '') and v2 = 10
+exec_multi(
+ 4, "FILTER",
+ [ 3, '>=', [ 'k1_1', '' ], 1000, 0, 'U', [ -1 ],
+ [ [ 'F', '=', 1, 10 ] ] ],
+);
+
+exec_multi(
+ 4, "VAL",
+ [ 1, '>=', [ '', '' ], 1000, 0 ],
+);
+# all
+
+exit 0;
+
+sub exec_single {
+ my ($width, $mess, $req) = @_;
+ print "$mess\n";
+ my $res = $hs->execute_single(@$req);
+ {
+ my $code = shift(@$res);
+ print "code=$code\n";
+ my $i = 0;
+ for my $fld (@$res) {
+ print "[$fld]";
+ if (++$i >= $width) {
+ print "\n";
+ $i = 0;
+ }
+ }
+ print "\n";
+ }
+}
+
+sub exec_multi {
+ my $width = shift(@_);
+ my $mess = shift(@_);
+ print "$mess\n";
+ my $mres = $hs->execute_multi(\@_);
+ for my $res (@$mres) {
+ my $code = shift(@$res);
+ print "code=$code\n";
+ my $i = 0;
+ for my $fld (@$res) {
+ print "[$fld]";
+ if (++$i >= $width) {
+ print "\n";
+ $i = 0;
+ }
+ }
+ print "\n";
+ }
+}
+
diff --git a/plugin/handler_socket/regtest/test_01_lib/test13.expected b/plugin/handler_socket/regtest/test_01_lib/test13.expected
new file mode 100644
index 00000000000..5c47447a746
--- /dev/null
+++ b/plugin/handler_socket/regtest/test_01_lib/test13.expected
@@ -0,0 +1,52 @@
+HSINSERT
+DUMP_TABLE
+1 v1sql_0 v2sql_0
+2 v1sql_1 v2sql_1
+3 v1sql_2 v2sql_2
+4 v1sql_3 v2sql_3
+5 v1sql_4 v2sql_4
+6 v1sql_5 v2sql_5
+7 v1sql_6 v2sql_6
+8 v1sql_7 v2sql_7
+9 v1sql_8 v2sql_8
+10 v1sql_9 v2sql_9
+11 v1hs_0 v2hs_0
+12 v1hs_1 v2hs_1
+13 v1hs_2 v2hs_2
+14 v1hs_3 v2hs_3
+15 v1hs_4 v2hs_4
+16 v1hs_5 v2hs_5
+17 v1hs_6 v2hs_6
+18 v1hs_7 v2hs_7
+19 v1hs_8 v2hs_8
+20 v1hs_9 v2hs_9
+21 v1hs3_0 v2hs3_0
+22 v1hs3_0 v2hs3_0
+23 v1hs3_0 v2hs3_0
+24 v1hs3_1 v2hs3_1
+25 v1hs3_1 v2hs3_1
+26 v1hs3_1 v2hs3_1
+27 v1hs3_2 v2hs3_2
+28 v1hs3_2 v2hs3_2
+29 v1hs3_2 v2hs3_2
+30 v1hs3_3 v2hs3_3
+31 v1hs3_3 v2hs3_3
+32 v1hs3_3 v2hs3_3
+33 v1hs3_4 v2hs3_4
+34 v1hs3_4 v2hs3_4
+35 v1hs3_4 v2hs3_4
+36 v1hs3_5 v2hs3_5
+37 v1hs3_5 v2hs3_5
+38 v1hs3_5 v2hs3_5
+39 v1hs3_6 v2hs3_6
+40 v1hs3_6 v2hs3_6
+41 v1hs3_6 v2hs3_6
+42 v1hs3_7 v2hs3_7
+43 v1hs3_7 v2hs3_7
+44 v1hs3_7 v2hs3_7
+45 v1hs3_8 v2hs3_8
+46 v1hs3_8 v2hs3_8
+47 v1hs3_8 v2hs3_8
+48 v1hs3_9 v2hs3_9
+49 v1hs3_9 v2hs3_9
+50 v1hs3_9 v2hs3_9
diff --git a/plugin/handler_socket/regtest/test_01_lib/test13.pl b/plugin/handler_socket/regtest/test_01_lib/test13.pl
new file mode 100644
index 00000000000..5b1440853b2
--- /dev/null
+++ b/plugin/handler_socket/regtest/test_01_lib/test13.pl
@@ -0,0 +1,86 @@
+#!/usr/bin/perl
+
+# vim:sw=2:ai
+
+# test for auto_increment
+
+BEGIN {
+ push @INC, "../common/";
+};
+
+use strict;
+use warnings;
+use hstest;
+
+my $dbh = hstest::init_testdb();
+my $table = 'hstesttbl';
+my $tablesize = 10;
+$dbh->do(
+ "create table $table (" .
+ "k int primary key auto_increment, " .
+ "v1 varchar(30), " .
+ "v2 varchar(30)) " .
+ "engine = myisam default charset = binary");
+srand(999);
+
+my $sth = $dbh->prepare("insert into $table values (?,?,?)");
+for (my $i = 0; $i < $tablesize; ++$i) {
+ my $k = 0;
+ my $v1 = "v1sql_" . $i;
+ my $v2 = "v2sql_" . $i;
+ $sth->execute($k, $v1, $v2);
+}
+
+my %valmap = ();
+
+print "HSINSERT\n";
+my $hs = hstest::get_hs_connection(undef, 9999);
+my $dbname = $hstest::conf{dbname};
+$hs->open_index(1, $dbname, $table, '', 'k,v1,v2');
+# inserts with auto_increment
+for (my $i = 0; $i < $tablesize; ++$i) {
+ my $k = 0;
+ my $v1 = "v1hs_" . $i;
+ my $v2 = "v2hs_" . $i;
+ my $r = $hs->execute_insert(1, [ $k, $v1, $v2 ]);
+ my $err = $r->[0];
+ if ($err != 0) {
+ my $err_str = $r->[1];
+ print "$err $err_str\n";
+ }
+}
+# make sure that it works even when inserts are pipelined. these requests
+# are possibly executed in a single transaction.
+for (my $i = 0; $i < $tablesize; ++$i) {
+ my $k = 0;
+ my $v1 = "v1hs3_" . $i;
+ my $v2 = "v2hs3_" . $i;
+ my $r = $hs->execute_multi([
+ [ 1, '+', [$k, $v1, $v2] ],
+ [ 1, '+', [$k, $v1, $v2] ],
+ [ 1, '+', [$k, $v1, $v2] ],
+ ]);
+ for (my $i = 0; $i < 3; ++$i) {
+ my $err = $r->[$i]->[0];
+ if ($err != 0) {
+ my $err_str = $r->[1];
+ print "$err $err_str\n";
+ }
+ }
+}
+undef $hs;
+
+dump_table();
+
+sub dump_table {
+ print "DUMP_TABLE\n";
+ my $aref = $dbh->selectall_arrayref("select k,v1,v2 from $table order by k");
+ for my $row (@$aref) {
+ my ($k, $v1, $v2) = @$row;
+ $v1 = "[null]" if !defined($v1);
+ $v2 = "[null]" if !defined($v2);
+ print "$k $v1 $v2\n";
+ # print "MISMATCH\n" if ($valmap{$k} ne $v);
+ }
+}
+
diff --git a/scripts/mysql_convert_table_format.sh b/scripts/mysql_convert_table_format.sh
index 6f586d0e8e0..7983982913c 100644
--- a/scripts/mysql_convert_table_format.sh
+++ b/scripts/mysql_convert_table_format.sh
@@ -28,7 +28,7 @@ $opt_port=0;
$exit_status=0;
GetOptions(
- "e|engine|type=s" => \$opt_type,
+ "e|engine|type=s" => \$opt_engine,
"f|force" => \$opt_force,
"help|?" => \$opt_help,
"h|host=s" => \$opt_host,
diff --git a/scripts/mysql_install_db.sh b/scripts/mysql_install_db.sh
index 2d3775afb3a..f863ddd749e 100644
--- a/scripts/mysql_install_db.sh
+++ b/scripts/mysql_install_db.sh
@@ -398,9 +398,14 @@ else
echo "Installation of system tables failed! Examine the logs in"
echo "$ldata for more information."
echo
- echo "You can try to start the mysqld daemon with:"
+ echo "The problem could be conflicting information in an external"
+ echo "my.cnf files. You can ignore these by doing:"
echo
- echo " shell> $mysqld --skip-grant &"
+ echo " shell> /scripts/mysql_install_db --defaults-file=~/.my.cnf"
+ echo
+ echo "You can also try to start the mysqld daemon with:"
+ echo
+ echo " shell> $mysqld --skip-grant --general-log &"
echo
echo "and use the command line tool $bindir/mysql"
echo "to connect to the mysql database and look at the grant tables:"
@@ -408,9 +413,6 @@ else
echo " shell> $bindir/mysql -u root mysql"
echo " mysql> show tables"
echo
- echo "Try 'mysqld --help' if you have problems with paths. Using"
- echo "--general-log gives you a log in $ldata that may be helpful."
- echo
echo "The latest information about mysql_install_db is available at"
echo "http://kb.askmonty.org/v/installing-system-tables-mysql_install_db."
echo "MariaDB is hosted on launchpad; You can find the latest source and"
diff --git a/sql-common/client.c b/sql-common/client.c
index 3788ca9829b..c449cd17100 100644
--- a/sql-common/client.c
+++ b/sql-common/client.c
@@ -1978,7 +1978,7 @@ static int send_change_user_packet(MCPVIO_EXT *mpvio,
char *buff, *end;
int res= 1;
- buff= my_alloca(USERNAME_LENGTH + data_len + 1 + NAME_LEN + 2 + NAME_LEN);
+ buff= my_alloca(USERNAME_LENGTH+1 + data_len+1 + NAME_LEN+1 + 2 + NAME_LEN+1);
end= strmake(buff, mysql->user, USERNAME_LENGTH) + 1;
diff --git a/sql/Makefile.am b/sql/Makefile.am
index afbef9ca197..05cb56bfab7 100644
--- a/sql/Makefile.am
+++ b/sql/Makefile.am
@@ -81,7 +81,7 @@ noinst_HEADERS = item.h item_func.h item_sum.h item_cmpfunc.h \
event_data_objects.h event_scheduler.h \
sql_partition.h partition_info.h partition_element.h \
contributors.h sql_servers.h \
- multi_range_read.h \
+ multi_range_read.h sql_handler.h \
sql_join_cache.h \
create_options.h \
sql_expression_cache.h
diff --git a/sql/ha_partition.h b/sql/ha_partition.h
index 1489ead8b5a..0cf1713ed13 100644
--- a/sql/ha_partition.h
+++ b/sql/ha_partition.h
@@ -253,7 +253,6 @@ public:
DBUG_RETURN(0);
}
virtual void change_table_ptr(TABLE *table_arg, TABLE_SHARE *share);
- bool check_if_supported_virtual_columns(void) { return TRUE;}
virtual bool check_if_incompatible_data(HA_CREATE_INFO *create_info,
uint table_changes);
private:
diff --git a/sql/handler.cc b/sql/handler.cc
index b49f29059b0..95dd4f71336 100644
--- a/sql/handler.cc
+++ b/sql/handler.cc
@@ -2743,7 +2743,10 @@ void handler::print_error(int error, myf errflag)
textno=ER_DUP_UNIQUE;
break;
case HA_ERR_RECORD_CHANGED:
- SET_FATAL_ERROR;
+ /*
+ This is not fatal error when using HANDLER interface
+ SET_FATAL_ERROR;
+ */
textno=ER_CHECKREAD;
break;
case HA_ERR_CRASHED:
diff --git a/sql/handler.h b/sql/handler.h
index 3beda9f7c88..23d64681d26 100644
--- a/sql/handler.h
+++ b/sql/handler.h
@@ -137,6 +137,7 @@
#define HA_BINLOG_STMT_CAPABLE (LL(1) << 35)
/* Has automatic checksums and uses the new checksum format */
#define HA_HAS_NEW_CHECKSUM (LL(1) << 36)
+#define HA_CAN_VIRTUAL_COLUMNS (LL(1) << 37)
#define HA_MRR_CANT_SORT (LL(1) << 37)
#define HA_RECORD_MUST_BE_CLEAN_ON_WRITE (LL(1) << 38)
@@ -1558,16 +1559,20 @@ public:
DBUG_ENTER("ha_index_init");
DBUG_ASSERT(inited==NONE);
if (!(result= index_init(idx, sorted)))
- inited=INDEX;
- end_range= NULL;
+ {
+ inited= INDEX;
+ active_index= idx;
+ end_range= NULL;
+ }
DBUG_RETURN(result);
}
int ha_index_end()
{
DBUG_ENTER("ha_index_end");
DBUG_ASSERT(inited==INDEX);
- inited=NONE;
- end_range= NULL;
+ inited= NONE;
+ active_index= MAX_KEY;
+ end_range= NULL;
DBUG_RETURN(index_end());
}
/* This is called after index_init() if we need to do a index scan */
@@ -1750,7 +1755,12 @@ public:
as there may be several calls to this routine.
*/
virtual void column_bitmaps_signal();
- uint get_index(void) const { return active_index; }
+ /*
+ We have to check for inited as some engines, like innodb, sets
+ active_index during table scan.
+ */
+ uint get_index(void) const
+ { return inited == INDEX ? active_index : MAX_KEY; }
virtual int close(void)=0;
/**
@@ -2003,6 +2013,7 @@ public:
{ return(NULL);} /* gets tablespace name from handler */
/** used in ALTER TABLE; 1 if changing storage engine is allowed */
virtual bool can_switch_engines() { return 1; }
+ virtual int can_continue_handler_scan() { return 0; }
/** used in REPLACE; is > 0 if table is referred by a FOREIGN KEY */
virtual int get_foreign_key_list(THD *thd, List<FOREIGN_KEY_INFO> *f_key_list)
{ return 0; }
@@ -2259,8 +2270,8 @@ private:
*/
virtual int open(const char *name, int mode, uint test_if_locked)=0;
- virtual int index_init(uint idx, bool sorted) { active_index= idx; return 0; }
- virtual int index_end() { active_index= MAX_KEY; return 0; }
+ virtual int index_init(uint idx, bool sorted) { return 0; }
+ virtual int index_end() { return 0; }
/**
rnd_init() can be called two times without rnd_end() in between
(it only makes sense if scan=1).
diff --git a/sql/item.h b/sql/item.h
index 08b283b6903..7c4b3c6e819 100644
--- a/sql/item.h
+++ b/sql/item.h
@@ -584,10 +584,17 @@ public:
virtual void fix_after_pullout(st_select_lex *new_parent, Item **ref) {};
/*
- should be used in case where we are sure that we do not need
+ This method should be used in case where we are sure that we do not need
complete fix_fields() procedure.
- */
- inline void quick_fix_field() { fixed= 1; }
+ Usually this method is used by the optimizer when it has to create a new
+ item out of other already fixed items. For example, if the optimizer has
+ to create a new Item_func for an inferred equality whose left and right
+ parts are already fixed items. In some cases the optimizer cannot use
+ directly fixed items as the arguments of the created functional item,
+ but rather uses intermediate type conversion items. Then the method is
+ supposed to be applied recursively.
+ */
+ virtual inline void quick_fix_field() { fixed= 1; }
/* Function returns 1 on overflow and -1 on fatal errors */
int save_in_field_no_warnings(Field *field, bool no_conversions);
virtual int save_in_field(Field *field, bool no_conversions);
diff --git a/sql/item_func.cc b/sql/item_func.cc
index 7404e99ba1c..dfad8a9af06 100644
--- a/sql/item_func.cc
+++ b/sql/item_func.cc
@@ -205,6 +205,21 @@ Item_func::fix_fields(THD *thd, Item **ref)
return FALSE;
}
+void
+Item_func::quick_fix_field()
+{
+ Item **arg,**arg_end;
+ if (arg_count)
+ {
+ for (arg=args, arg_end=args+arg_count; arg != arg_end ; arg++)
+ {
+ if (!(*arg)->fixed)
+ (*arg)->quick_fix_field();
+ }
+ }
+ fixed= 1;
+}
+
void Item_func::fix_after_pullout(st_select_lex *new_parent, Item **ref)
{
diff --git a/sql/item_func.h b/sql/item_func.h
index 0501b6f2299..9c0d827d582 100644
--- a/sql/item_func.h
+++ b/sql/item_func.h
@@ -118,6 +118,7 @@ public:
Item_func(THD *thd, Item_func *item);
bool fix_fields(THD *, Item **ref);
void fix_after_pullout(st_select_lex *new_parent, Item **ref);
+ void quick_fix_field();
table_map used_tables() const;
table_map not_null_tables() const;
void update_used_tables();
diff --git a/sql/lock.cc b/sql/lock.cc
index 8f5b5ac233f..4cab521fada 100644
--- a/sql/lock.cc
+++ b/sql/lock.cc
@@ -84,41 +84,10 @@
extern HASH open_cache;
-/* flags for get_lock_data */
-#define GET_LOCK_UNLOCK 1
-#define GET_LOCK_STORE_LOCKS 2
-
-static MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table,uint count,
- uint flags, TABLE **write_locked);
-static void reset_lock_data(MYSQL_LOCK *sql_lock);
static int lock_external(THD *thd, TABLE **table,uint count);
static int unlock_external(THD *thd, TABLE **table,uint count);
static void print_lock_error(int error, const char *);
-/*
- Lock tables.
-
- SYNOPSIS
- mysql_lock_tables()
- thd The current thread.
- tables An array of pointers to the tables to lock.
- count The number of tables to lock.
- flags Options:
- MYSQL_LOCK_IGNORE_GLOBAL_READ_LOCK Ignore a global read lock
- MYSQL_LOCK_IGNORE_GLOBAL_READ_ONLY Ignore SET GLOBAL READ_ONLY
- MYSQL_LOCK_IGNORE_FLUSH Ignore a flush tables.
- MYSQL_LOCK_NOTIFY_IF_NEED_REOPEN Instead of reopening altered
- or dropped tables by itself,
- mysql_lock_tables() should
- notify upper level and rely
- on caller doing this.
- need_reopen Out parameter, TRUE if some tables were altered
- or deleted and should be reopened by caller.
-
- RETURN
- A lock structure pointer on success.
- NULL on error or if some tables should be reopen.
-*/
/* Map the return value of thr_lock to an error from errmsg.txt */
static int thr_lock_errno_to_mysql[]=
@@ -132,6 +101,7 @@ static int thr_lock_errno_to_mysql[]=
@param flags Lock flags
@return 0 if all the check passed, non zero if a check failed.
*/
+
int mysql_lock_tables_check(THD *thd, TABLE **tables, uint count, uint flags)
{
bool log_table_write_query;
@@ -194,81 +164,118 @@ int mysql_lock_tables_check(THD *thd, TABLE **tables, uint count, uint flags)
DBUG_RETURN(0);
}
+
+/*
+ Lock tables.
+
+ SYNOPSIS
+ mysql_lock_tables()
+ thd The current thread.
+ tables An array of pointers to the tables to lock.
+ count The number of tables to lock.
+ flags Options:
+ MYSQL_LOCK_IGNORE_GLOBAL_READ_LOCK Ignore a global read lock
+ MYSQL_LOCK_IGNORE_GLOBAL_READ_ONLY Ignore SET GLOBAL READ_ONLY
+ MYSQL_LOCK_IGNORE_FLUSH Ignore a flush tables.
+ MYSQL_LOCK_NOTIFY_IF_NEED_REOPEN Instead of reopening altered
+ or dropped tables by itself,
+ mysql_lock_tables() should
+ notify upper level and rely
+ on caller doing this.
+ need_reopen Out parameter, TRUE if some tables were altered
+ or deleted and should be reopened by caller.
+
+ RETURN
+ A lock structure pointer on success.
+ NULL on error or if some tables should be reopen.
+*/
+
MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **tables, uint count,
uint flags, bool *need_reopen)
{
- MYSQL_LOCK *sql_lock;
TABLE *write_lock_used;
- int rc;
-
- DBUG_ENTER("mysql_lock_tables");
+ MYSQL_LOCK *sql_lock;
+ DBUG_ENTER("mysql_lock_tables(tables)");
*need_reopen= FALSE;
-
if (mysql_lock_tables_check(thd, tables, count, flags))
- DBUG_RETURN (NULL);
+ DBUG_RETURN(NULL);
- for (;;)
+ if (!(sql_lock= get_lock_data(thd, tables, count, GET_LOCK_STORE_LOCKS,
+ &write_lock_used)) ||
+ ! sql_lock->table_count)
+ DBUG_RETURN(sql_lock);
+
+ if (mysql_lock_tables(thd, sql_lock, write_lock_used != 0, flags,
+ need_reopen))
{
- if (! (sql_lock= get_lock_data(thd, tables, count, GET_LOCK_STORE_LOCKS,
- &write_lock_used)) ||
- ! sql_lock->table_count)
- break;
+ /* Clear the lock type of all lock data to avoid reusage. */
+ reset_lock_data(sql_lock, 1);
+ my_free(sql_lock, MYF(0));
+ sql_lock= 0;
+ }
+ DBUG_RETURN(sql_lock);
+}
+
+
+/**
+ Lock a table based on a MYSQL_LOCK structure.
- if (global_read_lock && write_lock_used &&
- ! (flags & MYSQL_LOCK_IGNORE_GLOBAL_READ_LOCK))
+ mysql_lock_tables()
+
+ @param thd The current thread.
+ @param sql_lock Tables that should be locked
+ @param write_lock_used 1 if any of the tables are write locked
+ @param flags See mysql_lock_tables()
+ @param need_reopen Out parameter, TRUE if some tables were altered
+ or deleted and should be reopened by caller.
+
+ @return 0 ok
+ @return 1 error
+*/
+
+bool mysql_lock_tables(THD *thd, MYSQL_LOCK *sql_lock,
+ bool write_lock_used,
+ uint flags, bool *need_reopen)
+{
+ int rc;
+ bool error= 1;
+ DBUG_ENTER("mysql_lock_tables(sql_lock)");
+
+ *need_reopen= FALSE;
+ for (;;)
+ {
+ if (write_lock_used && !(flags & MYSQL_LOCK_IGNORE_GLOBAL_READ_LOCK))
{
- /*
- Someone has issued LOCK ALL TABLES FOR READ and we want a write lock
- Wait until the lock is gone
- */
- if (wait_if_global_read_lock(thd, 1, 1))
+ if (global_read_lock)
{
- /* Clear the lock type of all lock data to avoid reusage. */
- reset_lock_data(sql_lock);
- my_free((uchar*) sql_lock,MYF(0));
- sql_lock=0;
- break;
+ /*
+ Someone has issued LOCK ALL TABLES FOR READ and we want a write lock
+ Wait until the lock is gone
+ */
+ if (wait_if_global_read_lock(thd, 1, 1))
+ break;
+ if (thd->version != refresh_version)
+ goto retry;
}
- if (thd->version != refresh_version)
+
+ if (opt_readonly &&
+ !(thd->security_ctx->master_access & SUPER_ACL) &&
+ !thd->slave_thread)
{
- /* Clear the lock type of all lock data to avoid reusage. */
- reset_lock_data(sql_lock);
- my_free((uchar*) sql_lock,MYF(0));
- goto retry;
+ /*
+ Someone has issued SET GLOBAL READ_ONLY=1 and we want a write lock.
+ We do not wait for READ_ONLY=0, and fail.
+ */
+ my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--read-only");
+ break;
}
}
- if (!(flags & MYSQL_LOCK_IGNORE_GLOBAL_READ_ONLY) &&
- write_lock_used &&
- opt_readonly &&
- !(thd->security_ctx->master_access & SUPER_ACL) &&
- !thd->slave_thread)
- {
- /*
- Someone has issued SET GLOBAL READ_ONLY=1 and we want a write lock.
- We do not wait for READ_ONLY=0, and fail.
- */
- reset_lock_data(sql_lock);
- my_free((uchar*) sql_lock, MYF(0));
- sql_lock=0;
- my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--read-only");
- break;
- }
-
thd_proc_info(thd, "System lock");
- DBUG_PRINT("info", ("thd->proc_info %s", thd->proc_info));
if (lock_external(thd, sql_lock->table, sql_lock->table_count))
- {
- /* Clear the lock type of all lock data to avoid reusage. */
- reset_lock_data(sql_lock);
- my_free((uchar*) sql_lock,MYF(0));
- sql_lock=0;
break;
- }
thd_proc_info(thd, "Table lock");
- DBUG_PRINT("info", ("thd->proc_info %s", thd->proc_info));
- thd->locked=1;
/* Copy the lock data array. thr_multi_lock() reorders its contens. */
memcpy(sql_lock->locks + sql_lock->lock_count, sql_lock->locks,
sql_lock->lock_count * sizeof(*sql_lock->locks));
@@ -277,70 +284,66 @@ MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **tables, uint count,
sql_lock->lock_count,
sql_lock->lock_count,
thd->lock_id)];
- if (rc > 1) /* a timeout or a deadlock */
+ if (rc) /* Locking failed */
{
VOID(unlock_external(thd, sql_lock->table, sql_lock->table_count));
- my_error(rc, MYF(0));
- my_free((uchar*) sql_lock,MYF(0));
- sql_lock= 0;
- break;
- }
- else if (rc == 1) /* aborted */
- {
- /*
- reset_lock_data is required here. If thr_multi_lock fails it
- resets lock type for tables, which were locked before (and
- including) one that caused error. Lock type for other tables
- preserved.
- */
- reset_lock_data(sql_lock);
- thd->some_tables_deleted=1; // Try again
- sql_lock->lock_count= 0; // Locks are already freed
+ if (rc > 1)
+ {
+ /* a timeout or a deadlock */
+ my_error(rc, MYF(0));
+ break;
+ }
+ /* We where aborted and should try again from upper level*/
+ thd->some_tables_deleted= 1;
}
- else if (!thd->some_tables_deleted || (flags & MYSQL_LOCK_IGNORE_FLUSH))
+ else
{
/*
- Thread was killed or lock aborted. Let upper level close all
- used tables and retry or give error.
+ Lock worked. Now check that nothing happend while we where waiting
+ to get the lock that would require us to free it.
*/
- thd->locked=0;
- break;
- }
- else if (!thd->open_tables)
- {
- // Only using temporary tables, no need to unlock
- thd->some_tables_deleted=0;
- thd->locked=0;
- break;
+ error= 0;
+ if (!thd->some_tables_deleted || (flags & MYSQL_LOCK_IGNORE_FLUSH))
+ {
+ /*
+ Table was not signaled for deletion or we don't care if it was.
+ Return with table as locked.
+ */
+ break;
+ }
+ else if (!thd->open_tables && !(flags & MYSQL_LOCK_NOT_TEMPORARY))
+ {
+ /*
+ Only using temporary tables, no need to unlock.
+ We need the flag as open_tables is not enough to distingush if
+ we are only using temporary tables for tables used trough
+ the HANDLER interface.
+
+ We reset some_tables_deleted as it doesn't make sense to have this
+ one when we are only using temporary tables.
+ */
+ thd->some_tables_deleted=0;
+ break;
+ }
+ /* some table was altered or deleted. reopen tables marked deleted */
+ error= 1;
+ mysql_unlock_tables(thd, sql_lock, 0);
}
- thd_proc_info(thd, 0);
- /* some table was altered or deleted. reopen tables marked deleted */
- mysql_unlock_tables(thd,sql_lock);
- thd->locked=0;
retry:
- sql_lock=0;
if (flags & MYSQL_LOCK_NOTIFY_IF_NEED_REOPEN)
{
*need_reopen= TRUE;
break;
}
if (wait_for_tables(thd))
- break; // Couldn't open tables
- }
- thd_proc_info(thd, 0);
- if (thd->killed)
- {
- thd->send_kill_message();
- if (sql_lock)
- {
- mysql_unlock_tables(thd,sql_lock);
- sql_lock=0;
- }
+ break; // Couldn't open tables
+ reset_lock_data(sql_lock, 0); // Set org locks and retry
}
+ thd_proc_info(thd, 0);
thd->set_time_after_lock();
- DBUG_RETURN (sql_lock);
+ DBUG_RETURN(error);
}
@@ -380,15 +383,15 @@ static int lock_external(THD *thd, TABLE **tables, uint count)
DBUG_RETURN(0);
}
-
-void mysql_unlock_tables(THD *thd, MYSQL_LOCK *sql_lock)
+void mysql_unlock_tables(THD *thd, MYSQL_LOCK *sql_lock, bool free_lock)
{
DBUG_ENTER("mysql_unlock_tables");
if (sql_lock->table_count)
VOID(unlock_external(thd,sql_lock->table,sql_lock->table_count));
if (sql_lock->lock_count)
thr_multi_unlock(sql_lock->locks,sql_lock->lock_count, 0);
- my_free((uchar*) sql_lock,MYF(0));
+ if (free_lock)
+ my_free((uchar*) sql_lock,MYF(0));
DBUG_VOID_RETURN;
}
@@ -847,12 +850,12 @@ static int unlock_external(THD *thd, TABLE **table,uint count)
@param write_lock_used Store pointer to last table with WRITE_ALLOW_WRITE
*/
-static MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table_ptr, uint count,
- uint flags, TABLE **write_lock_used)
+MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table_ptr, uint count,
+ uint flags, TABLE **write_lock_used)
{
uint i,tables,lock_count;
MYSQL_LOCK *sql_lock;
- THR_LOCK_DATA **locks, **locks_buf, **locks_start;
+ THR_LOCK_DATA **locks, **locks_buf;
TABLE **to, **table_buf;
DBUG_ENTER("get_lock_data");
@@ -891,7 +894,7 @@ static MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table_ptr, uint count,
{
TABLE *table;
enum thr_lock_type lock_type;
-
+ THR_LOCK_DATA **locks_start;
if ((table=table_ptr[i])->s->tmp_table == NON_TRANSACTIONAL_TMP_TABLE)
continue;
lock_type= table->reginfo.lock_type;
@@ -904,12 +907,11 @@ static MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table_ptr, uint count,
my_error(ER_OPEN_AS_READONLY,MYF(0),table->alias.c_ptr());
/* Clear the lock type of the lock data that are stored already. */
sql_lock->lock_count= (uint) (locks - sql_lock->locks);
- reset_lock_data(sql_lock);
+ reset_lock_data(sql_lock, 1);
my_free((uchar*) sql_lock,MYF(0));
DBUG_RETURN(0);
}
}
- THR_LOCK_DATA **org_locks = locks;
locks_start= locks;
locks= table->file->store_lock(thd, locks,
(flags & GET_LOCK_UNLOCK) ? TL_IGNORE :
@@ -922,8 +924,13 @@ static MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table_ptr, uint count,
}
*to++= table;
if (locks)
- for ( ; org_locks != locks ; org_locks++)
- (*org_locks)->debug_print_param= (void *) table;
+ {
+ for ( ; locks_start != locks ; locks_start++)
+ {
+ (*locks_start)->debug_print_param= (void *) table;
+ (*locks_start)->org_type= (*locks_start)->type;
+ }
+ }
}
/*
We do not use 'tables', because there are cases where store_lock()
@@ -964,10 +971,13 @@ static MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table_ptr, uint count,
Clear the lock type of all lock data. This ensures that the next
lock request will set its lock type properly.
- @param sql_lock The MySQL lock.
+ @param sql_lock The MySQL lock.
+ @param unlock If set, then set lock type to TL_UNLOCK,
+ otherwise set to original lock type from
+ get_store_lock().
*/
-static void reset_lock_data(MYSQL_LOCK *sql_lock)
+void reset_lock_data(MYSQL_LOCK *sql_lock, bool unlock)
{
THR_LOCK_DATA **ldata;
THR_LOCK_DATA **ldata_end;
@@ -975,10 +985,7 @@ static void reset_lock_data(MYSQL_LOCK *sql_lock)
for (ldata= sql_lock->locks, ldata_end= ldata + sql_lock->lock_count;
ldata < ldata_end;
ldata++)
- {
- /* Reset lock type. */
- (*ldata)->type= TL_UNLOCK;
- }
+ (*ldata)->type= unlock ? TL_UNLOCK : (*ldata)->org_type;
}
diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h
index 6ecbb1fe1df..08607fd41fc 100644
--- a/sql/mysql_priv.h
+++ b/sql/mysql_priv.h
@@ -1493,15 +1493,6 @@ void mysqld_stmt_reset(THD *thd, char *packet);
void mysql_stmt_get_longdata(THD *thd, char *pos, ulong packet_length);
void reinit_stmt_before_use(THD *thd, LEX *lex);
-/* sql_handler.cc */
-bool mysql_ha_open(THD *thd, TABLE_LIST *tables, bool reopen);
-bool mysql_ha_close(THD *thd, TABLE_LIST *tables);
-bool mysql_ha_read(THD *, TABLE_LIST *,enum enum_ha_read_modes,char *,
- List<Item> *,enum ha_rkey_function,Item *,ha_rows,ha_rows);
-void mysql_ha_flush(THD *thd);
-void mysql_ha_rm_tables(THD *thd, TABLE_LIST *tables, bool is_locked);
-void mysql_ha_cleanup(THD *thd);
-
/* sql_base.cc */
#define TMP_TABLE_KEY_EXTRA 8
void set_item_name(Item *item,char *pos,uint length);
@@ -2201,6 +2192,10 @@ extern struct st_VioSSLFd * ssl_acceptor_fd;
MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **table, uint count,
uint flags, bool *need_reopen);
+bool mysql_lock_tables(THD *thd, MYSQL_LOCK *sql_lock,
+ bool write_lock_used,
+ uint flags, bool *need_reopen);
+
/* mysql_lock_tables() and open_table() flags bits */
#define MYSQL_LOCK_IGNORE_GLOBAL_READ_LOCK 0x0001
#define MYSQL_LOCK_IGNORE_FLUSH 0x0002
@@ -2208,8 +2203,12 @@ MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **table, uint count,
#define MYSQL_OPEN_TEMPORARY_ONLY 0x0008
#define MYSQL_LOCK_IGNORE_GLOBAL_READ_ONLY 0x0010
#define MYSQL_LOCK_PERF_SCHEMA 0x0020
+#define MYSQL_LOCK_NOT_TEMPORARY 0x0040
+/* flags for get_lock_data */
+#define GET_LOCK_UNLOCK 1
+#define GET_LOCK_STORE_LOCKS 2
-void mysql_unlock_tables(THD *thd, MYSQL_LOCK *sql_lock);
+void mysql_unlock_tables(THD *thd, MYSQL_LOCK *sql_lock, bool free_lock= 1);
void mysql_unlock_read_tables(THD *thd, MYSQL_LOCK *sql_lock);
void mysql_unlock_some_tables(THD *thd, TABLE **table,uint count);
void mysql_lock_remove(THD *thd, MYSQL_LOCK *locked,TABLE *table,
@@ -2221,6 +2220,7 @@ bool mysql_lock_abort_for_thread(THD *thd, TABLE *table);
MYSQL_LOCK *mysql_lock_merge(MYSQL_LOCK *a,MYSQL_LOCK *b);
TABLE_LIST *mysql_lock_have_duplicate(THD *thd, TABLE_LIST *needle,
TABLE_LIST *haystack);
+void reset_lock_data(MYSQL_LOCK *sql_lock, bool unlock);
bool lock_global_read_lock(THD *thd);
void unlock_global_read_lock(THD *thd);
bool wait_if_global_read_lock(THD *thd, bool abort_on_refresh,
@@ -2230,6 +2230,8 @@ bool make_global_read_lock_block_commit(THD *thd);
bool set_protect_against_global_read_lock(void);
void unset_protect_against_global_read_lock(void);
void broadcast_refresh(void);
+MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table_ptr, uint count,
+ uint flags, TABLE **write_lock_used);
/* Lock based on name */
int lock_and_wait_for_table_name(THD *thd, TABLE_LIST *table_list);
diff --git a/sql/mysqld.cc b/sql/mysqld.cc
index 7bd16724d22..b0247b1d48b 100644
--- a/sql/mysqld.cc
+++ b/sql/mysqld.cc
@@ -460,7 +460,7 @@ static bool volatile ready_to_exit;
static my_bool opt_debugging= 0, opt_external_locking= 0, opt_console= 0;
static my_bool opt_short_log_format= 0;
static my_bool opt_ignore_wrong_options= 0, opt_expect_abort= 0;
-static my_bool opt_sync= 0;
+static my_bool opt_sync= 0, opt_thread_alarm;
static uint kill_cached_threads, wake_thread;
ulong thread_created;
uint thread_handling;
@@ -6008,7 +6008,7 @@ enum options_mysqld
OPT_RANGE_ALLOC_BLOCK_SIZE, OPT_ALLOW_SUSPICIOUS_UDFS,
OPT_QUERY_ALLOC_BLOCK_SIZE, OPT_QUERY_PREALLOC_SIZE,
OPT_TRANS_ALLOC_BLOCK_SIZE, OPT_TRANS_PREALLOC_SIZE,
- OPT_SYNC_FRM, OPT_SYNC_BINLOG, OPT_SYNC,
+ OPT_SYNC_FRM, OPT_SYNC_BINLOG, OPT_SYNC, OPT_THREAD_ALARM,
OPT_SYNC_REPLICATION,
OPT_SYNC_REPLICATION_SLAVE_ID,
OPT_SYNC_REPLICATION_TIMEOUT,
@@ -6333,7 +6333,7 @@ struct my_option my_long_options[] =
"Disable initialization of builtin InnoDB plugin.",
0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
{"init-connect", OPT_INIT_CONNECT,
- "Command(s) that are executed for each new connection.",
+ "Command(s) that are executed for each new connection (but not for SUPER users).",
&opt_init_connect, &opt_init_connect, 0, GET_STR_ALLOC,
REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
#ifndef DISABLE_GRANT_OPTIONS
@@ -7585,6 +7585,10 @@ thread is in the relay logs.",
"error. Used only if the connection has active cursors.",
&table_lock_wait_timeout, &table_lock_wait_timeout,
0, GET_ULONG, REQUIRED_ARG, 50, 1, 1024 * 1024 * 1024, 0, 1, 0},
+ {"thread-alarm", OPT_THREAD_ALARM,
+ "Enable/disable system thread alarm calls. Should only be turned off when running tests or debugging!!",
+ &opt_thread_alarm, &opt_thread_alarm, 0, GET_BOOL, NO_ARG, 1, 0, 0, 0, 0,
+ 0},
{"thread_cache_size", OPT_THREAD_CACHE_SIZE,
"How many threads we should keep in a cache for reuse.",
&thread_cache_size, &thread_cache_size, 0, GET_ULONG,
@@ -9364,6 +9368,7 @@ static int get_options(int *argc,char **argv)
*/
my_disable_locking= myisam_single_user= test(opt_external_locking == 0);
my_disable_sync= opt_sync == 0;
+ my_disable_thr_alarm= opt_thread_alarm == 0;
my_default_record_cache_size=global_system_variables.read_buff_size;
myisam_max_temp_length=
(my_off_t) global_system_variables.myisam_max_sort_file_size;
diff --git a/sql/net_serv.cc b/sql/net_serv.cc
index 3441d1e120a..1b313da8ce1 100644
--- a/sql/net_serv.cc
+++ b/sql/net_serv.cc
@@ -697,7 +697,8 @@ net_real_write(NET *net,const uchar *packet, size_t len)
{
my_bool old_mode;
thr_end_alarm(&alarmed);
- vio_blocking(net->vio, net_blocking, &old_mode);
+ if (!net_blocking)
+ vio_blocking(net->vio, net_blocking, &old_mode);
}
net->reading_or_writing=0;
DBUG_RETURN(((int) (pos != end)));
@@ -988,7 +989,8 @@ end:
{
my_bool old_mode;
thr_end_alarm(&alarmed);
- vio_blocking(net->vio, net_blocking, &old_mode);
+ if (!net_blocking)
+ vio_blocking(net->vio, net_blocking, &old_mode);
}
net->reading_or_writing=0;
#ifdef DEBUG_DATA_PACKETS
diff --git a/sql/share/errmsg.txt b/sql/share/errmsg.txt
index 54061931d5f..a872fa1f1e9 100644
--- a/sql/share/errmsg.txt
+++ b/sql/share/errmsg.txt
@@ -6017,7 +6017,7 @@ ER_ONLY_INTEGERS_ALLOWED
eng "Only integers allowed as number here"
ger "An dieser Stelle sind nur Ganzzahlen zulässig"
ER_UNSUPORTED_LOG_ENGINE
- eng "This storage engine cannot be used for log tables""
+ eng "This storage engine cannot be used for log tables"
ger "Diese Speicher-Engine kann für Logtabellen nicht verwendet werden"
ER_BAD_LOG_STATEMENT
eng "You cannot '%s' a log table if logging is enabled"
@@ -6236,13 +6236,15 @@ ER_WARNING_NON_DEFAULT_VALUE_FOR_VIRTUAL_COLUMN
eng "The value specified for computed column '%s' in table '%s' ignored"
ER_UNSUPPORTED_ACTION_ON_VIRTUAL_COLUMN
- eng "'%s' is not yet supported for computed columns"
+ eng "This is not yet supported for computed columns"
ER_CONST_EXPR_IN_VCOL
eng "Constant expression in computed column function is not allowed"
ER_ROW_EXPR_FOR_VCOL
eng "Expression for computed column cannot return a row"
+ER_UNSUPPORTED_ENGINE_FOR_VIRTUAL_COLUMNS
+ eng "%s storage engine does not support computed columns"
ER_UNKNOWN_OPTION
eng "Unknown option '%-.64s'"
ER_BAD_OPTION_VALUE
diff --git a/sql/sql_base.cc b/sql/sql_base.cc
index 530ebf85955..01c72db0c8e 100644
--- a/sql/sql_base.cc
+++ b/sql/sql_base.cc
@@ -22,6 +22,7 @@
#include "sp_head.h"
#include "sp.h"
#include "sql_trigger.h"
+#include "sql_handler.h"
#include <m_ctype.h>
#include <my_dir.h>
#include <hash.h>
diff --git a/sql/sql_class.cc b/sql/sql_class.cc
index 084e2c8d78c..11447389186 100644
--- a/sql/sql_class.cc
+++ b/sql/sql_class.cc
@@ -44,6 +44,7 @@
#include "sp_cache.h"
#include "sql_select.h" /* declares create_tmp_table() */
#include "debug_sync.h"
+#include "sql_handler.h"
/*
The following is used to initialise Table_ident with a internal
@@ -702,7 +703,7 @@ THD::THD()
catalog= (char*)"std"; // the only catalog we have for now
main_security_ctx.init();
security_ctx= &main_security_ctx;
- locked=some_tables_deleted=no_errors=password= 0;
+ some_tables_deleted=no_errors=password= 0;
query_start_used= 0;
count_cuted_fields= CHECK_FIELD_IGNORE;
killed= NOT_KILLED;
diff --git a/sql/sql_class.h b/sql/sql_class.h
index 6bee43cebc7..7e6aa086f4c 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -1944,7 +1944,7 @@ public:
bool slave_thread, one_shot_set;
/* tells if current statement should binlog row-based(1) or stmt-based(0) */
bool current_stmt_binlog_row_based;
- bool locked, some_tables_deleted;
+ bool some_tables_deleted;
bool last_cuted_field;
bool no_errors, password;
bool extra_port; /* If extra connection */
diff --git a/sql/sql_db.cc b/sql/sql_db.cc
index 7aa48524b20..b5478287fda 100644
--- a/sql/sql_db.cc
+++ b/sql/sql_db.cc
@@ -20,6 +20,7 @@
#include <mysys_err.h>
#include "sp.h"
#include "events.h"
+#include "sql_handler.h"
#include <my_dir.h>
#include <m_ctype.h>
#include "log.h"
diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc
index 5564d628594..e2cb17090a1 100644
--- a/sql/sql_delete.cc
+++ b/sql/sql_delete.cc
@@ -23,6 +23,7 @@
#include "sql_select.h"
#include "sp_head.h"
#include "sql_trigger.h"
+#include "sql_handler.h"
/**
Implement DELETE SQL word.
diff --git a/sql/sql_handler.cc b/sql/sql_handler.cc
index 69be8c8e9b4..7e651a2b4e2 100644
--- a/sql/sql_handler.cc
+++ b/sql/sql_handler.cc
@@ -56,9 +56,13 @@
second container. When the table is flushed, the pointer is cleared.
*/
+#ifdef USE_PRAGMA_IMPLEMENTATION
+#pragma implementation // gcc: Class implementation
+#endif
+
#include "mysql_priv.h"
#include "sql_select.h"
-#include <assert.h>
+#include "sql_handler.h"
#define HANDLER_TABLES_HASH_SIZE 120
@@ -66,6 +70,28 @@ static enum enum_ha_read_modes rkey_to_rnext[]=
{ RNEXT_SAME, RNEXT, RPREV, RNEXT, RPREV, RNEXT, RPREV, RPREV };
/*
+ Set handler to state after create, but keep base information about
+ which table is used
+*/
+
+void SQL_HANDLER::reset()
+{
+ fields.empty();
+ arena.free_items();
+ free_root(&mem_root, MYF(0));
+ my_free(lock, MYF(MY_ALLOW_ZERO_PTR));
+ init();
+}
+
+/* Free all allocated data */
+
+SQL_HANDLER::~SQL_HANDLER()
+{
+ reset();
+ my_free(base_data, MYF(MY_ALLOW_ZERO_PTR));
+}
+
+/*
Get hash key and hash key length.
SYNOPSIS
@@ -84,11 +110,11 @@ static enum enum_ha_read_modes rkey_to_rnext[]=
Pointer to the TABLE_LIST struct.
*/
-static char *mysql_ha_hash_get_key(TABLE_LIST *tables, size_t *key_len_p,
+static char *mysql_ha_hash_get_key(SQL_HANDLER *table, size_t *key_len,
my_bool first __attribute__((unused)))
{
- *key_len_p= strlen(tables->alias) + 1 ; /* include '\0' in comparisons */
- return tables->alias;
+ *key_len= table->handler_name.length + 1 ; /* include '\0' in comparisons */
+ return table->handler_name.str;
}
@@ -106,9 +132,9 @@ static char *mysql_ha_hash_get_key(TABLE_LIST *tables, size_t *key_len_p,
Nothing
*/
-static void mysql_ha_hash_free(TABLE_LIST *tables)
+static void mysql_ha_hash_free(SQL_HANDLER *table)
{
- my_free((char*) tables, MYF(0));
+ delete table;
}
/**
@@ -120,14 +146,21 @@ static void mysql_ha_hash_free(TABLE_LIST *tables)
@note Though this function takes a list of tables, only the first list entry
will be closed.
+ @mote handler_object is not deleted!
@note Broadcasts refresh if it closed a table with old version.
*/
-static void mysql_ha_close_table(THD *thd, TABLE_LIST *tables,
+static void mysql_ha_close_table(SQL_HANDLER *handler,
bool is_locked)
{
+ THD *thd= handler->thd;
+ TABLE *table= handler->table;
TABLE **table_ptr;
+ /* check if table was already closed */
+ if (!table)
+ return;
+
/*
Though we could take the table pointer from hash_tables->table,
we must follow the thd->handler_tables chain anyway, as we need the
@@ -135,13 +168,18 @@ static void mysql_ha_close_table(THD *thd, TABLE_LIST *tables,
for close_thread_table().
*/
for (table_ptr= &(thd->handler_tables);
- *table_ptr && (*table_ptr != tables->table);
+ *table_ptr && (*table_ptr != table);
table_ptr= &(*table_ptr)->next)
;
if (*table_ptr)
{
- (*table_ptr)->file->ha_index_or_rnd_end();
+ if (handler->lock)
+ {
+ // Mark it unlocked, like in reset_lock_data()
+ reset_lock_data(handler->lock, 1);
+ }
+ table->file->ha_index_or_rnd_end();
if (! is_locked)
VOID(pthread_mutex_lock(&LOCK_open));
if (close_thread_table(thd, table_ptr))
@@ -152,17 +190,15 @@ static void mysql_ha_close_table(THD *thd, TABLE_LIST *tables,
if (! is_locked)
VOID(pthread_mutex_unlock(&LOCK_open));
}
- else if (tables->table)
+ else
{
/* Must be a temporary table */
- TABLE *table= tables->table;
table->file->ha_index_or_rnd_end();
table->query_id= thd->query_id;
table->open_by_handler= 0;
}
-
- /* Mark table as closed, ready for re-open if necessary. */
- tables->table= NULL;
+ my_free(handler->lock, MYF(MY_ALLOW_ZERO_PTR));
+ handler->init();
}
/*
@@ -178,7 +214,7 @@ static void mysql_ha_close_table(THD *thd, TABLE_LIST *tables,
Though this function takes a list of tables, only the first list entry
will be opened.
'reopen' is set when a handler table is to be re-opened. In this case,
- 'tables' is the pointer to the hashed TABLE_LIST object which has been
+ 'tables' is the pointer to the hashed SQL_HANDLER object which has been
saved on the original open.
'reopen' is also used to suppress the sending of an 'ok' message.
@@ -187,17 +223,17 @@ static void mysql_ha_close_table(THD *thd, TABLE_LIST *tables,
TRUE Error
*/
-bool mysql_ha_open(THD *thd, TABLE_LIST *tables, bool reopen)
+bool mysql_ha_open(THD *thd, TABLE_LIST *tables, SQL_HANDLER *reopen)
{
- TABLE_LIST *hash_tables = NULL;
- char *db, *name, *alias;
- uint dblen, namelen, aliaslen, counter;
+ SQL_HANDLER *sql_handler= 0;
+ uint counter;
int error;
- TABLE *backup_open_tables;
+ TABLE *table, *backup_open_tables, *write_lock_used;
+ Query_arena backup_arena;
DBUG_ENTER("mysql_ha_open");
DBUG_PRINT("enter",("'%s'.'%s' as '%s' reopen: %d",
tables->db, tables->table_name, tables->alias,
- (int) reopen));
+ reopen != 0));
if (tables->schema_table)
{
@@ -210,7 +246,7 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, bool reopen)
if (! hash_inited(&thd->handler_tables_hash))
{
/*
- HASH entries are of type TABLE_LIST.
+ HASH entries are of type SQL_HANDLER
*/
if (hash_init(&thd->handler_tables_hash, &my_charset_latin1,
HANDLER_TABLES_HASH_SIZE, 0, 0,
@@ -288,8 +324,10 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, bool reopen)
if (error)
goto err;
+ table= tables->table;
+
/* There can be only one table in '*tables'. */
- if (! (tables->table->file->ha_table_flags() & HA_CAN_SQL_HANDLER))
+ if (! (table->file->ha_table_flags() & HA_CAN_SQL_HANDLER))
{
my_error(ER_ILLEGAL_HA, MYF(0), tables->alias);
goto err;
@@ -297,36 +335,69 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, bool reopen)
if (! reopen)
{
- /* copy the TABLE_LIST struct */
- dblen= strlen(tables->db) + 1;
- namelen= strlen(tables->table_name) + 1;
- aliaslen= strlen(tables->alias) + 1;
- if (!(my_multi_malloc(MYF(MY_WME),
- &hash_tables, (uint) sizeof(*hash_tables),
- &db, (uint) dblen,
- &name, (uint) namelen,
- &alias, (uint) aliaslen,
+ /* copy data to sql_handler */
+ if (!(sql_handler= new SQL_HANDLER(thd)))
+ goto err;
+ init_alloc_root(&sql_handler->mem_root, 1024, 0);
+
+ sql_handler->table= table;
+ sql_handler->db.length= strlen(tables->db);
+ sql_handler->table_name.length= strlen(tables->table_name);
+ sql_handler->handler_name.length= strlen(tables->alias);
+
+ if (!(my_multi_malloc(MY_WME,
+ &sql_handler->db.str,
+ (uint) sql_handler->db.length + 1,
+ &sql_handler->table_name.str,
+ (uint) sql_handler->table_name.length + 1,
+ &sql_handler->handler_name.str,
+ (uint) sql_handler->handler_name.length + 1,
NullS)))
goto err;
- /* structure copy */
- *hash_tables= *tables;
- hash_tables->db= db;
- hash_tables->table_name= name;
- hash_tables->alias= alias;
- memcpy(hash_tables->db, tables->db, dblen);
- memcpy(hash_tables->table_name, tables->table_name, namelen);
- memcpy(hash_tables->alias, tables->alias, aliaslen);
+ sql_handler->base_data= sql_handler->db.str; // Free this
+ memcpy(sql_handler->db.str, tables->db, sql_handler->db.length +1);
+ memcpy(sql_handler->table_name.str, tables->table_name,
+ sql_handler->table_name.length+1);
+ memcpy(sql_handler->handler_name.str, tables->alias,
+ sql_handler->handler_name.length +1);
/* add to hash */
- if (my_hash_insert(&thd->handler_tables_hash, (uchar*) hash_tables))
+ if (my_hash_insert(&thd->handler_tables_hash, (uchar*) sql_handler))
goto err;
}
+ else
+ {
+ sql_handler= reopen;
+ sql_handler->reset();
+ }
+ sql_handler->table= table;
+
+ if (!(sql_handler->lock= get_lock_data(thd, &sql_handler->table, 1,
+ GET_LOCK_STORE_LOCKS,
+ &write_lock_used)))
+ goto err;
+
+ /* Get a list of all fields for send_fields */
+ thd->set_n_backup_active_arena(&sql_handler->arena, &backup_arena);
+ error= table->fill_item_list(&sql_handler->fields);
+ thd->restore_active_arena(&sql_handler->arena, &backup_arena);
+
+ if (error)
+ {
+ if (reopen)
+ sql_handler= 0;
+ goto err;
+ }
+
+ /* Always read all columns */
+ table->read_set= &table->s->all_set;
+ table->vcol_set= &table->s->all_set;
/*
If it's a temp table, don't reset table->query_id as the table is
being used by this handler. Otherwise, no meaning at all.
*/
- tables->table->open_by_handler= 1;
+ table->open_by_handler= 1;
if (! reopen)
my_ok(thd);
@@ -334,10 +405,13 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, bool reopen)
DBUG_RETURN(FALSE);
err:
- if (hash_tables)
- my_free((char*) hash_tables, MYF(0));
+ delete sql_handler;
if (tables->table)
- mysql_ha_close_table(thd, tables, FALSE);
+ {
+ SQL_HANDLER tmp_sql_handler(thd);
+ tmp_sql_handler.table= tables->table;
+ mysql_ha_close_table(&tmp_sql_handler, FALSE);
+ }
DBUG_PRINT("exit",("ERROR"));
DBUG_RETURN(TRUE);
}
@@ -362,17 +436,17 @@ err:
bool mysql_ha_close(THD *thd, TABLE_LIST *tables)
{
- TABLE_LIST *hash_tables;
+ SQL_HANDLER *handler;
DBUG_ENTER("mysql_ha_close");
DBUG_PRINT("enter",("'%s'.'%s' as '%s'",
tables->db, tables->table_name, tables->alias));
- if ((hash_tables= (TABLE_LIST*) hash_search(&thd->handler_tables_hash,
- (uchar*) tables->alias,
- strlen(tables->alias) + 1)))
+ if ((handler= (SQL_HANDLER*) hash_search(&thd->handler_tables_hash,
+ (uchar*) tables->alias,
+ strlen(tables->alias) + 1)))
{
- mysql_ha_close_table(thd, hash_tables, FALSE);
- hash_delete(&thd->handler_tables_hash, (uchar*) hash_tables);
+ mysql_ha_close_table(handler, FALSE);
+ hash_delete(&thd->handler_tables_hash, (uchar*) handler);
}
else
{
@@ -387,6 +461,167 @@ bool mysql_ha_close(THD *thd, TABLE_LIST *tables)
}
+/**
+ Finds an open HANDLER table.
+
+ @params name Name of handler to open
+
+ @return 0 failure
+ @return handler
+*/
+
+SQL_HANDLER *mysql_ha_find_handler(THD *thd, const char *name)
+{
+ SQL_HANDLER *handler;
+ if ((handler= (SQL_HANDLER*) hash_search(&thd->handler_tables_hash,
+ (uchar*) name,
+ strlen(name) + 1)))
+ {
+ DBUG_PRINT("info-in-hash",("'%s'.'%s' as '%s' table: %p",
+ handler->db.str,
+ handler->table_name.str,
+ handler->handler_name.str, handler->table));
+ if (!handler->table)
+ {
+ /* The handler table has been closed. Re-open it. */
+ TABLE_LIST tmp;
+ tmp.init_one_table(handler->db.str, handler->table_name.str,
+ TL_READ);
+ tmp.alias= handler->handler_name.str;
+
+ if (mysql_ha_open(thd, &tmp, handler))
+ {
+ DBUG_PRINT("exit",("reopen failed"));
+ return 0;
+ }
+ }
+ }
+ else
+ {
+ my_error(ER_UNKNOWN_TABLE, MYF(0), name, "HANDLER");
+ return 0;
+ }
+ return handler;
+}
+
+
+/**
+ Check that condition and key name are ok
+
+ @param handler
+ @param mode Read mode (RFIRST, RNEXT etc...)
+ @param keyname Key to use.
+ @param key_expr List of key column values
+ @param cond Where clause
+ @param in_prepare If we are in prepare phase (we can't evalute items yet)
+
+ @return 0 ok
+ @return 1 error
+
+ In ok, then values of used key and mode is stored in sql_handler
+*/
+
+static bool
+mysql_ha_fix_cond_and_key(SQL_HANDLER *handler,
+ enum enum_ha_read_modes mode, char *keyname,
+ List<Item> *key_expr,
+ Item *cond, bool in_prepare)
+{
+ THD *thd= handler->thd;
+ TABLE *table= handler->table;
+ if (cond)
+ {
+ /* This can only be true for temp tables */
+ if (table->query_id != thd->query_id)
+ cond->cleanup(); // File was reopened
+ if ((!cond->fixed &&
+ cond->fix_fields(thd, &cond)) || cond->check_cols(1))
+ return 1;
+ }
+
+ if (keyname)
+ {
+ /* Check if same as last keyname. If not, do a full lookup */
+ if (handler->keyno < 0 ||
+ my_strcasecmp(&my_charset_latin1,
+ keyname,
+ table->s->key_info[handler->keyno].name))
+ {
+ if ((handler->keyno= find_type(keyname, &table->s->keynames, 1+2)-1)<0)
+ {
+ my_error(ER_KEY_DOES_NOT_EXITS, MYF(0), keyname,
+ handler->handler_name);
+ return 1;
+ }
+ }
+
+ /* Check key parts */
+ if (mode == RKEY)
+ {
+ TABLE *table= handler->table;
+ KEY *keyinfo= table->key_info + handler->keyno;
+ KEY_PART_INFO *key_part= keyinfo->key_part;
+ List_iterator<Item> it_ke(*key_expr);
+ Item *item;
+ key_part_map keypart_map;
+ uint key_len;
+
+ if (key_expr->elements > keyinfo->key_parts)
+ {
+ my_error(ER_TOO_MANY_KEY_PARTS, MYF(0), keyinfo->key_parts);
+ return 1;
+ }
+ for (keypart_map= key_len=0 ; (item=it_ke++) ; key_part++)
+ {
+ my_bitmap_map *old_map;
+ /* note that 'item' can be changed by fix_fields() call */
+ if ((!item->fixed &&
+ item->fix_fields(thd, it_ke.ref())) ||
+ (item= *it_ke.ref())->check_cols(1))
+ return 1;
+ if (item->used_tables() & ~(RAND_TABLE_BIT | PARAM_TABLE_BIT))
+ {
+ my_error(ER_WRONG_ARGUMENTS,MYF(0),"HANDLER ... READ");
+ return 1;
+ }
+ if (!in_prepare)
+ {
+ old_map= dbug_tmp_use_all_columns(table, table->write_set);
+ (void) item->save_in_field(key_part->field, 1);
+ dbug_tmp_restore_column_map(table->write_set, old_map);
+ }
+ key_len+= key_part->store_length;
+ keypart_map= (keypart_map << 1) | 1;
+ }
+ handler->keypart_map= keypart_map;
+ handler->key_len= key_len;
+ }
+ else
+ {
+ /*
+ Check if the same index involved.
+ We need to always do this check because we may not have yet
+ called the handler since the last keyno change.
+ */
+ if ((uint) handler->keyno != table->file->get_index())
+ {
+ if (mode == RNEXT)
+ mode= RFIRST;
+ else if (mode == RPREV)
+ mode= RLAST;
+ }
+ }
+ }
+ else if (table->file->inited != handler::RND)
+ {
+ /* Convert RNEXT to RFIRST if we haven't started row scan */
+ if (mode == RNEXT)
+ mode= RFIRST;
+ }
+ handler->mode= mode; // Store adjusted mode
+ return 0;
+}
+
/*
Read from a HANDLER table.
@@ -413,147 +648,76 @@ bool mysql_ha_read(THD *thd, TABLE_LIST *tables,
enum ha_rkey_function ha_rkey_mode, Item *cond,
ha_rows select_limit_cnt, ha_rows offset_limit_cnt)
{
- TABLE_LIST *hash_tables;
- TABLE *table, *backup_open_tables;
- MYSQL_LOCK *lock;
+ SQL_HANDLER *handler;
+ TABLE *table;
List<Item> list;
Protocol *protocol= thd->protocol;
char buff[MAX_FIELD_WIDTH];
String buffer(buff, sizeof(buff), system_charset_info);
- int error, keyno= -1;
+ int error, keyno;
uint num_rows;
uchar *UNINIT_VAR(key);
- uint UNINIT_VAR(key_len);
bool need_reopen;
+ List_iterator<Item> it;
DBUG_ENTER("mysql_ha_read");
DBUG_PRINT("enter",("'%s'.'%s' as '%s'",
tables->db, tables->table_name, tables->alias));
- thd->lex->select_lex.context.resolve_in_table_list_only(tables);
- list.push_front(new Item_field(&thd->lex->select_lex.context,
- NULL, NULL, "*"));
- List_iterator<Item> it(list);
- it++;
retry:
- if ((hash_tables= (TABLE_LIST*) hash_search(&thd->handler_tables_hash,
- (uchar*) tables->alias,
- strlen(tables->alias) + 1)))
+ if (!(handler= mysql_ha_find_handler(thd, tables->alias)))
+ goto err0;
+
+ table= handler->table;
+ tables->table= table; // This is used by fix_fields
+
+ /* save open_tables state */
+ if (handler->lock->lock_count > 0)
{
- table= hash_tables->table;
- DBUG_PRINT("info-in-hash",("'%s'.'%s' as '%s' table: 0x%lx",
- hash_tables->db, hash_tables->table_name,
- hash_tables->alias, (long) table));
- if (!table)
+ bool lock_error;
+
+ handler->lock->locks[0]->type= handler->lock->locks[0]->org_type;
+ lock_error= mysql_lock_tables(thd, handler->lock, 0,
+ (MYSQL_LOCK_NOTIFY_IF_NEED_REOPEN |
+ (handler->table->s->tmp_table ==
+ NO_TMP_TABLE ?
+ MYSQL_LOCK_NOT_TEMPORARY : 0)),
+ &need_reopen);
+ if (need_reopen)
{
- /*
- The handler table has been closed. Re-open it.
- */
- if (mysql_ha_open(thd, hash_tables, 1))
+ mysql_ha_close_table(handler, FALSE);
+ if (thd->stmt_arena->is_stmt_execute())
{
- DBUG_PRINT("exit",("reopen failed"));
+ /*
+ As we have already sent field list and types to the client, we can't
+ handle any changes in the table format for prepared statements.
+ Better to force a reprepare.
+ */
+ my_error(ER_NEED_REPREPARE, MYF(0));
goto err0;
}
- table= hash_tables->table;
- DBUG_PRINT("info",("re-opened '%s'.'%s' as '%s' tab %p",
- hash_tables->db, hash_tables->table_name,
- hash_tables->alias, table));
- }
-
-#if MYSQL_VERSION_ID < 40100
- if (*tables->db && strcmp(table->table_cache_key, tables->db))
- {
- DBUG_PRINT("info",("wrong db"));
- table= NULL;
+ /*
+ The lock might have been aborted, we need to manually reset
+ thd->some_tables_deleted because handler's tables are closed
+ in a non-standard way. Otherwise we might loop indefinitely.
+ */
+ thd->some_tables_deleted= 0;
+ goto retry;
}
-#endif
- }
- else
- table= NULL;
-
- if (!table)
- {
-#if MYSQL_VERSION_ID < 40100
- char buff[MAX_DBKEY_LENGTH];
- if (*tables->db)
- strxnmov(buff, sizeof(buff)-1, tables->db, ".", tables->table_name,
- NullS);
- else
- strncpy(buff, tables->alias, sizeof(buff));
- my_error(ER_UNKNOWN_TABLE, MYF(0), buff, "HANDLER");
-#else
- my_error(ER_UNKNOWN_TABLE, MYF(0), tables->alias, "HANDLER");
-#endif
- goto err0;
- }
- tables->table=table;
-
- /* save open_tables state */
- backup_open_tables= thd->open_tables;
- /*
- mysql_lock_tables() needs thd->open_tables to be set correctly to
- be able to handle aborts properly. When the abort happens, it's
- safe to not protect thd->handler_tables because it won't close any
- tables.
- */
- thd->open_tables= thd->handler_tables;
-
- lock= mysql_lock_tables(thd, &tables->table, 1,
- MYSQL_LOCK_NOTIFY_IF_NEED_REOPEN, &need_reopen);
-
- /* restore previous context */
- thd->open_tables= backup_open_tables;
-
- if (need_reopen)
- {
- mysql_ha_close_table(thd, hash_tables, FALSE);
- /*
- The lock might have been aborted, we need to manually reset
- thd->some_tables_deleted because handler's tables are closed
- in a non-standard way. Otherwise we might loop indefinitely.
- */
- thd->some_tables_deleted= 0;
- goto retry;
- }
-
- if (!lock)
- goto err0; // mysql_lock_tables() printed error message already
-
- // Always read all columns
- tables->table->read_set= &tables->table->s->all_set;
-
- if (cond)
- {
- if (table->query_id != thd->query_id)
- cond->cleanup(); // File was reopened
- if ((!cond->fixed &&
- cond->fix_fields(thd, &cond)) || cond->check_cols(1))
- goto err;
- }
- if (keyname)
- {
- if ((keyno=find_type(keyname, &table->s->keynames, 1+2)-1)<0)
- {
- my_error(ER_KEY_DOES_NOT_EXITS, MYF(0), keyname, tables->alias);
- goto err;
- }
- /* Check if the same index involved. */
- if ((uint) keyno != table->file->get_index())
- {
- if (mode == RNEXT)
- mode= RFIRST;
- else if (mode == RPREV)
- mode= RLAST;
- }
+ if (lock_error)
+ goto err0; // mysql_lock_tables() printed error message already
}
- if (insert_fields(thd, &thd->lex->select_lex.context,
- tables->db, tables->alias, &it, 0))
+ if (mysql_ha_fix_cond_and_key(handler, mode, keyname, key_expr, cond, 0))
goto err;
+ mode= handler->mode;
+ keyno= handler->keyno;
- protocol->send_fields(&list, Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF);
+ it.init(handler->fields);
+ protocol->send_fields(&handler->fields,
+ Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF);
/*
In ::external_lock InnoDB resets the fields which tell it that
@@ -569,6 +733,8 @@ retry:
case RNEXT:
if (table->file->inited != handler::NONE)
{
+ if ((error= table->file->can_continue_handler_scan()))
+ break;
if (keyname)
{
/* Check if we read from the same index. */
@@ -576,9 +742,7 @@ retry:
error= table->file->ha_index_next(table->record[0]);
}
else
- {
error= table->file->ha_rnd_next(table->record[0]);
- }
break;
}
/* else fall through */
@@ -595,7 +759,7 @@ retry:
if (!(error= table->file->ha_rnd_init(1)))
error= table->file->ha_rnd_next(table->record[0]);
}
- mode=RNEXT;
+ mode= RNEXT;
break;
case RPREV:
DBUG_ASSERT(keyname != 0);
@@ -603,7 +767,9 @@ retry:
DBUG_ASSERT((uint) keyno == table->file->get_index());
if (table->file->inited != handler::NONE)
{
- error=table->file->ha_index_prev(table->record[0]);
+ if ((error= table->file->can_continue_handler_scan()))
+ break;
+ error= table->file->ha_index_prev(table->record[0]);
break;
}
/* else fall through */
@@ -612,54 +778,28 @@ retry:
table->file->ha_index_or_rnd_end();
table->file->ha_index_init(keyno, 1);
error= table->file->ha_index_last(table->record[0]);
- mode=RPREV;
+ mode= RPREV;
break;
case RNEXT_SAME:
/* Continue scan on "(keypart1,keypart2,...)=(c1, c2, ...) */
DBUG_ASSERT(keyname != 0);
- error= table->file->ha_index_next_same(table->record[0], key, key_len);
+ error= table->file->ha_index_next_same(table->record[0], key,
+ handler->key_len);
break;
case RKEY:
{
DBUG_ASSERT(keyname != 0);
- KEY *keyinfo=table->key_info+keyno;
- KEY_PART_INFO *key_part=keyinfo->key_part;
- if (key_expr->elements > keyinfo->key_parts)
- {
- my_error(ER_TOO_MANY_KEY_PARTS, MYF(0), keyinfo->key_parts);
- goto err;
- }
- List_iterator<Item> it_ke(*key_expr);
- Item *item;
- key_part_map keypart_map;
- for (keypart_map= key_len=0 ; (item=it_ke++) ; key_part++)
- {
- my_bitmap_map *old_map;
- // 'item' can be changed by fix_fields() call
- if ((!item->fixed &&
- item->fix_fields(thd, it_ke.ref())) ||
- (item= *it_ke.ref())->check_cols(1))
- goto err;
- if (item->used_tables() & ~RAND_TABLE_BIT)
- {
- my_error(ER_WRONG_ARGUMENTS,MYF(0),"HANDLER ... READ");
- goto err;
- }
- old_map= dbug_tmp_use_all_columns(table, table->write_set);
- (void) item->save_in_field(key_part->field, 1);
- dbug_tmp_restore_column_map(table->write_set, old_map);
- key_len+=key_part->store_length;
- keypart_map= (keypart_map << 1) | 1;
- }
- if (!(key= (uchar*) thd->calloc(ALIGN_SIZE(key_len))))
+ if (!(key= (uchar*) thd->calloc(ALIGN_SIZE(handler->key_len))))
goto err;
table->file->ha_index_or_rnd_end();
table->file->ha_index_init(keyno, 1);
- key_copy(key, table->record[0], table->key_info + keyno, key_len);
+ key_copy(key, table->record[0], table->key_info + keyno,
+ handler->key_len);
error= table->file->ha_index_read_map(table->record[0],
- key, keypart_map, ha_rkey_mode);
- mode=rkey_to_rnext[(int)ha_rkey_mode];
+ key, handler->keypart_map,
+ ha_rkey_mode);
+ mode= rkey_to_rnext[(int)ha_rkey_mode];
break;
}
default:
@@ -673,9 +813,13 @@ retry:
continue;
if (error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE)
{
- sql_print_error("mysql_ha_read: Got error %d when reading table '%s'",
- error, tables->table_name);
+ /* Don't give error in the log file for some expected problems */
+ if (error != HA_ERR_RECORD_CHANGED && error != HA_ERR_WRONG_COMMAND)
+ sql_print_error("mysql_ha_read: Got error %d when reading "
+ "table '%s'",
+ error, tables->table_name);
table->file->print_error(error,MYF(0));
+ table->file->ha_index_or_rnd_end();
goto err;
}
goto ok;
@@ -703,13 +847,13 @@ retry:
num_rows++;
}
ok:
- mysql_unlock_tables(thd,lock);
+ mysql_unlock_tables(thd, handler->lock, 0);
my_eof(thd);
DBUG_PRINT("exit",("OK"));
DBUG_RETURN(FALSE);
err:
- mysql_unlock_tables(thd,lock);
+ mysql_unlock_tables(thd, handler->lock, 0);
err0:
DBUG_PRINT("exit",("ERROR"));
DBUG_RETURN(TRUE);
@@ -717,6 +861,28 @@ err0:
/**
+ Prepare for handler read
+
+ For parameters, see mysql_ha_read()
+*/
+
+SQL_HANDLER *mysql_ha_read_prepare(THD *thd, TABLE_LIST *tables,
+ enum enum_ha_read_modes mode, char *keyname,
+ List<Item> *key_expr, Item *cond)
+{
+ SQL_HANDLER *handler;
+ DBUG_ENTER("mysql_ha_read_prepare");
+ if (!(handler= mysql_ha_find_handler(thd, tables->alias)))
+ DBUG_RETURN(0);
+ tables->table= handler->table; // This is used by fix_fields
+ if (mysql_ha_fix_cond_and_key(handler, mode, keyname, key_expr, cond, 1))
+ DBUG_RETURN(0);
+ DBUG_RETURN(handler);
+}
+
+
+
+/**
Scan the handler tables hash for matching tables.
@param thd Thread identifier.
@@ -727,30 +893,32 @@ err0:
table was matched.
*/
-static TABLE_LIST *mysql_ha_find(THD *thd, TABLE_LIST *tables)
+static SQL_HANDLER *mysql_ha_find_match(THD *thd, TABLE_LIST *tables)
{
- TABLE_LIST *hash_tables, *head= NULL, *first= tables;
- DBUG_ENTER("mysql_ha_find");
+ SQL_HANDLER *hash_tables, *head= NULL;
+ TABLE_LIST *first= tables;
+ DBUG_ENTER("mysql_ha_find_match");
/* search for all handlers with matching table names */
for (uint i= 0; i < thd->handler_tables_hash.records; i++)
{
- hash_tables= (TABLE_LIST*) hash_element(&thd->handler_tables_hash, i);
+ hash_tables= (SQL_HANDLER*) hash_element(&thd->handler_tables_hash, i);
+
for (tables= first; tables; tables= tables->next_local)
{
if ((! *tables->db ||
- ! my_strcasecmp(&my_charset_latin1, hash_tables->db, tables->db)) &&
- ! my_strcasecmp(&my_charset_latin1, hash_tables->table_name,
+ ! my_strcasecmp(&my_charset_latin1, hash_tables->db.str,
+ tables->db)) &&
+ ! my_strcasecmp(&my_charset_latin1, hash_tables->table_name.str,
tables->table_name))
+ {
+ /* Link into hash_tables list */
+ hash_tables->next= head;
+ head= hash_tables;
break;
- }
- if (tables)
- {
- hash_tables->next_local= head;
- head= hash_tables;
+ }
}
}
-
DBUG_RETURN(head);
}
@@ -767,18 +935,18 @@ static TABLE_LIST *mysql_ha_find(THD *thd, TABLE_LIST *tables)
void mysql_ha_rm_tables(THD *thd, TABLE_LIST *tables, bool is_locked)
{
- TABLE_LIST *hash_tables, *next;
+ SQL_HANDLER *hash_tables, *next;
DBUG_ENTER("mysql_ha_rm_tables");
DBUG_ASSERT(tables);
- hash_tables= mysql_ha_find(thd, tables);
+ hash_tables= mysql_ha_find_match(thd, tables);
while (hash_tables)
{
- next= hash_tables->next_local;
+ next= hash_tables->next;
if (hash_tables->table)
- mysql_ha_close_table(thd, hash_tables, is_locked);
+ mysql_ha_close_table(hash_tables, is_locked);
hash_delete(&thd->handler_tables_hash, (uchar*) hash_tables);
hash_tables= next;
}
@@ -798,16 +966,16 @@ void mysql_ha_rm_tables(THD *thd, TABLE_LIST *tables, bool is_locked)
void mysql_ha_flush(THD *thd)
{
- TABLE_LIST *hash_tables;
+ SQL_HANDLER *hash_tables;
DBUG_ENTER("mysql_ha_flush");
safe_mutex_assert_owner(&LOCK_open);
for (uint i= 0; i < thd->handler_tables_hash.records; i++)
{
- hash_tables= (TABLE_LIST*) hash_element(&thd->handler_tables_hash, i);
+ hash_tables= (SQL_HANDLER*) hash_element(&thd->handler_tables_hash, i);
if (hash_tables->table && hash_tables->table->needs_reopen_or_name_lock())
- mysql_ha_close_table(thd, hash_tables, TRUE);
+ mysql_ha_close_table(hash_tables, TRUE);
}
DBUG_VOID_RETURN;
@@ -824,14 +992,14 @@ void mysql_ha_flush(THD *thd)
void mysql_ha_cleanup(THD *thd)
{
- TABLE_LIST *hash_tables;
+ SQL_HANDLER *hash_tables;
DBUG_ENTER("mysql_ha_cleanup");
for (uint i= 0; i < thd->handler_tables_hash.records; i++)
{
- hash_tables= (TABLE_LIST*) hash_element(&thd->handler_tables_hash, i);
+ hash_tables= (SQL_HANDLER*) hash_element(&thd->handler_tables_hash, i);
if (hash_tables->table)
- mysql_ha_close_table(thd, hash_tables, FALSE);
+ mysql_ha_close_table(hash_tables, FALSE);
}
hash_free(&thd->handler_tables_hash);
diff --git a/sql/sql_handler.h b/sql/sql_handler.h
new file mode 100644
index 00000000000..54e72e9f50e
--- /dev/null
+++ b/sql/sql_handler.h
@@ -0,0 +1,61 @@
+/* Copyright (C) 2010 Monty Program Ab
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#ifdef USE_PRAGMA_INTERFACE
+#pragma interface /* gcc class implementation */
+#endif
+
+/* Open handlers are stored here */
+
+class SQL_HANDLER {
+public:
+ TABLE *table;
+ List<Item> fields; /* Fields, set on open */
+ THD *thd;
+ LEX_STRING handler_name;
+ LEX_STRING db;
+ LEX_STRING table_name;
+ MEM_ROOT mem_root;
+ MYSQL_LOCK *lock;
+
+ key_part_map keypart_map;
+ int keyno; /* Used key */
+ uint key_len;
+ enum enum_ha_read_modes mode;
+
+ /* This is only used when deleting many handler objects */
+ SQL_HANDLER *next;
+
+ Query_arena arena;
+ char *base_data;
+ SQL_HANDLER(THD *thd_arg) :
+ thd(thd_arg), arena(&mem_root, Query_arena::INITIALIZED)
+ { init(); clear_alloc_root(&mem_root); base_data= 0; }
+ void init() { keyno= -1; table= 0; lock= 0; }
+ void reset();
+
+ ~SQL_HANDLER();
+};
+
+bool mysql_ha_open(THD *thd, TABLE_LIST *tables, SQL_HANDLER *reopen);
+bool mysql_ha_close(THD *thd, TABLE_LIST *tables);
+bool mysql_ha_read(THD *, TABLE_LIST *,enum enum_ha_read_modes,char *,
+ List<Item> *,enum ha_rkey_function,Item *,ha_rows,ha_rows);
+void mysql_ha_flush(THD *thd);
+void mysql_ha_rm_tables(THD *thd, TABLE_LIST *tables, bool is_locked);
+void mysql_ha_cleanup(THD *thd);
+
+SQL_HANDLER *mysql_ha_read_prepare(THD *thd, TABLE_LIST *tables,
+ enum enum_ha_read_modes mode, char *keyname,
+ List<Item> *key_expr, Item *cond);
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index 9212017d955..2482775d5cd 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -28,6 +28,7 @@
#include "events.h"
#include "sql_trigger.h"
#include "debug_sync.h"
+#include "sql_handler.h"
#ifdef WITH_ARIA_STORAGE_ENGINE
#include "../storage/maria/ha_maria.h"
@@ -3186,12 +3187,17 @@ end_with_restore_list:
DBUG_EXECUTE_IF("after_mysql_insert",
{
- const char act[]=
+ const char act1[]=
"now "
"wait_for signal.continue";
+ const char act2[]=
+ "now "
+ "signal signal.continued";
DBUG_ASSERT(opt_debug_sync_timeout > 0);
- DBUG_ASSERT(!debug_sync_set_action(current_thd,
- STRING_WITH_LEN(act)));
+ DBUG_ASSERT(!debug_sync_set_action(thd,
+ STRING_WITH_LEN(act1)));
+ DBUG_ASSERT(!debug_sync_set_action(thd,
+ STRING_WITH_LEN(act2)));
};);
break;
}
diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc
index a1243589ed1..9bfa8d61e0a 100644
--- a/sql/sql_prepare.cc
+++ b/sql/sql_prepare.cc
@@ -95,6 +95,7 @@ When one supplies long data for a placeholder:
#else
#include <mysql_com.h>
#endif
+#include "sql_handler.h"
/**
A result class used to send cursor rows using the binary protocol.
@@ -243,6 +244,8 @@ static bool send_prep_stmt(Prepared_statement *stmt, uint columns)
int error;
THD *thd= stmt->thd;
DBUG_ENTER("send_prep_stmt");
+ DBUG_PRINT("enter",("stmt->id: %lu columns: %d param_count: %d",
+ stmt->id, columns, stmt->param_count));
buff[0]= 0; /* OK packet indicator */
int4store(buff+1, stmt->id);
@@ -1835,6 +1838,56 @@ static bool mysql_test_insert_select(Prepared_statement *stmt,
return res;
}
+/**
+ Validate SELECT statement.
+
+ In case of success, if this query is not EXPLAIN, send column list info
+ back to the client.
+
+ @param stmt prepared statement
+ @param tables list of tables used in the query
+
+ @retval 0 success
+ @retval 1 error, error message is set in THD
+ @retval 2 success, and statement metadata has been sent
+*/
+
+static int mysql_test_handler_read(Prepared_statement *stmt,
+ TABLE_LIST *tables)
+{
+ THD *thd= stmt->thd;
+ LEX *lex= stmt->lex;
+ SQL_HANDLER *ha_table;
+ DBUG_ENTER("mysql_test_select");
+
+ lex->select_lex.context.resolve_in_select_list= TRUE;
+
+ /*
+ We don't have to test for permissions as this is already done during
+ HANDLER OPEN
+ */
+ if (!(ha_table= mysql_ha_read_prepare(thd, tables, lex->ha_read_mode,
+ lex->ident.str,
+ lex->insert_list,
+ lex->select_lex.where)))
+ DBUG_RETURN(1);
+
+ if (!stmt->is_sql_prepare())
+ {
+ if (!lex->result && !(lex->result= new (stmt->mem_root) select_send))
+ {
+ my_error(ER_OUTOFMEMORY, MYF(0), sizeof(select_send));
+ DBUG_RETURN(1);
+ }
+ if (send_prep_stmt(stmt, ha_table->fields.elements) ||
+ lex->result->send_fields(ha_table->fields, Protocol::SEND_EOF) ||
+ thd->protocol->flush())
+ DBUG_RETURN(1);
+ DBUG_RETURN(2);
+ }
+ DBUG_RETURN(0);
+}
+
/**
Perform semantic analysis of the parsed tree and send a response packet
@@ -1949,6 +2002,11 @@ static bool check_prepared_statement(Prepared_statement *stmt)
res= mysql_test_insert_select(stmt, tables);
break;
+ case SQLCOM_HA_READ:
+ res= mysql_test_handler_read(stmt, tables);
+ /* Statement and field info has already been sent */
+ DBUG_RETURN(res == 1 ? TRUE : FALSE);
+
/*
Note that we don't need to have cases in this list if they are
marked with CF_STATUS_COMMAND in sql_command_flags
diff --git a/sql/sql_rename.cc b/sql/sql_rename.cc
index e85e730db5b..5c8d1add04b 100644
--- a/sql/sql_rename.cc
+++ b/sql/sql_rename.cc
@@ -19,7 +19,7 @@
#include "mysql_priv.h"
#include "sql_trigger.h"
-
+#include "sql_handler.h"
static TABLE_LIST *rename_tables(THD *thd, TABLE_LIST *table_list,
bool skip_error);
diff --git a/sql/sql_select.cc b/sql/sql_select.cc
index 7b1f8b31ad5..35f1b772568 100644
--- a/sql/sql_select.cc
+++ b/sql/sql_select.cc
@@ -5535,7 +5535,7 @@ optimize_straight_join(JOIN *join, table_map join_tables)
All other cases are in-between these two extremes. Thus the parameter
'search_depth' controlls the exhaustiveness of the search. The higher the
- value, the longer the optimizaton time and possibly the better the
+ value, the longer the optimization time and possibly the better the
resulting plan. The lower the value, the fewer alternative plans are
estimated, but the more likely to get a bad QEP.
@@ -13119,6 +13119,11 @@ create_internal_tmp_table_from_heap2(THD *thd, TABLE *table,
DBUG_EXECUTE_IF("raise_error", write_err= HA_ERR_FOUND_DUPP_KEY ;);
if (write_err)
goto err;
+ if (thd->killed)
+ {
+ thd->send_kill_message();
+ goto err_killed;
+ }
}
if (!new_table.no_rows && new_table.file->ha_end_bulk_insert())
goto err;
@@ -13151,6 +13156,7 @@ create_internal_tmp_table_from_heap2(THD *thd, TABLE *table,
err:
DBUG_PRINT("error",("Got error: %d",write_err));
table->file->print_error(write_err, MYF(0));
+err_killed:
(void) table->file->ha_rnd_end();
(void) new_table.file->close();
err1:
diff --git a/sql/sql_show.cc b/sql/sql_show.cc
index cdfb982b372..61f3709476c 100644
--- a/sql/sql_show.cc
+++ b/sql/sql_show.cc
@@ -1942,8 +1942,7 @@ void mysqld_list_processes(THD *thd,const char *user, bool verbose)
pthread_mutex_lock(&mysys_var->mutex);
thd_info->proc_info= (char*) (tmp->killed == THD::KILL_CONNECTION? "Killed" : 0);
#ifndef EMBEDDED_LIBRARY
- thd_info->state_info= (char*) (tmp->locked ? "Locked" :
- tmp->net.reading_or_writing ?
+ thd_info->state_info= (char*) (tmp->net.reading_or_writing ?
(tmp->net.reading_or_writing == 2 ?
"Writing to net" :
thd_info->command == COM_SLEEP ? "" :
@@ -2068,8 +2067,7 @@ int fill_schema_processlist(THD* thd, TABLE_LIST* tables, COND* cond)
table->field[5]->store(utime / 1000000, TRUE);
/* STATE */
#ifndef EMBEDDED_LIBRARY
- val= (char*) (tmp->locked ? "Locked" :
- tmp->net.reading_or_writing ?
+ val= (char*) (tmp->net.reading_or_writing ?
(tmp->net.reading_or_writing == 2 ?
"Writing to net" :
tmp->command == COM_SLEEP ? "" :
diff --git a/sql/sql_table.cc b/sql/sql_table.cc
index 05a9c66cf80..7138c82d891 100644
--- a/sql/sql_table.cc
+++ b/sql/sql_table.cc
@@ -24,6 +24,7 @@
#include "sql_trigger.h"
#include "sql_show.h"
#include "debug_sync.h"
+#include "sql_handler.h"
#ifdef __WIN__
#include <io.h>
@@ -3929,12 +3930,16 @@ bool mysql_create_table_no_lock(THD *thd,
}
/* Give warnings for not supported table options */
- if (create_info->transactional && !file->ht->commit)
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
- ER_ILLEGAL_HA_CREATE_OPTION,
- ER(ER_ILLEGAL_HA_CREATE_OPTION),
- file->engine_name()->str,
- "TRANSACTIONAL=1");
+#if defined(WITH_ARIA_STORAGE_ENGINE)
+ extern handlerton *maria_hton;
+ if (file->ht != maria_hton)
+#endif
+ if (create_info->transactional)
+ push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
+ ER_ILLEGAL_HA_CREATE_OPTION,
+ ER(ER_ILLEGAL_HA_CREATE_OPTION),
+ file->engine_name()->str,
+ "TRANSACTIONAL=1");
VOID(pthread_mutex_lock(&LOCK_open));
if (!internal_tmp_table && !(create_info->options & HA_LEX_CREATE_TMP_TABLE))
@@ -6276,9 +6281,7 @@ mysql_prepare_alter_table(THD *thd, TABLE *table,
def->field=field;
if (field->stored_in_db != def->stored_in_db)
{
- my_error(ER_UNSUPPORTED_ACTION_ON_VIRTUAL_COLUMN,
- MYF(0),
- "Changing the STORED status");
+ my_error(ER_UNSUPPORTED_ACTION_ON_VIRTUAL_COLUMN, MYF(0));
goto err;
}
if (!def->after)
@@ -7226,6 +7229,16 @@ view_err:
/* Non-primary unique key. */
needed_online_flags|= HA_ONLINE_ADD_UNIQUE_INDEX;
needed_fast_flags|= HA_ONLINE_ADD_UNIQUE_INDEX_NO_WRITES;
+ if (ignore)
+ {
+ /*
+ If ignore is used, we have to remove all duplicate rows,
+ which require a full table copy.
+ */
+ need_copy_table= ALTER_TABLE_DATA_CHANGED;
+ pk_changed= 2; // Don't change need_copy_table
+ break;
+ }
}
}
else
diff --git a/sql/sql_trigger.cc b/sql/sql_trigger.cc
index 1c4add27e57..8b95a36bc35 100644
--- a/sql/sql_trigger.cc
+++ b/sql/sql_trigger.cc
@@ -19,6 +19,7 @@
#include "sp_head.h"
#include "sql_trigger.h"
#include "parse_file.h"
+#include "sql_handler.h"
/*************************************************************************/
diff --git a/sql/table.cc b/sql/table.cc
index d13c595f078..db10ddc8a14 100644
--- a/sql/table.cc
+++ b/sql/table.cc
@@ -2327,14 +2327,11 @@ partititon_err:
/* Check virtual columns against table's storage engine. */
if (share->vfields &&
- ((outparam->file &&
- !outparam->file->check_if_supported_virtual_columns()) ||
- (!outparam->file && share->db_type() &&
- share->db_type()->db_type == DB_TYPE_CSV_DB))) // Workaround for CSV
- {
- my_error(ER_UNSUPPORTED_ACTION_ON_VIRTUAL_COLUMN,
- MYF(0),
- "Specified storage engine");
+ !(outparam->file &&
+ (outparam->file->ha_table_flags() & HA_CAN_VIRTUAL_COLUMNS)))
+ {
+ my_error(ER_UNSUPPORTED_ENGINE_FOR_VIRTUAL_COLUMNS, MYF(0),
+ plugin_name(share->db_plugin)->str);
error_reported= TRUE;
goto err;
}
diff --git a/sql/table.h b/sql/table.h
index ed90cfb9527..79082651052 100644
--- a/sql/table.h
+++ b/sql/table.h
@@ -1816,7 +1816,7 @@ typedef struct st_nested_join
2. All child join nest nodes are fully covered.
*/
- bool is_fully_covered() const { return join_list.elements == counter; }
+ bool is_fully_covered() const { return n_tables == counter; }
} NESTED_JOIN;
diff --git a/storage/federated/ha_federated.cc b/storage/federated/ha_federated.cc
index fcc178b09a6..34509e04799 100644
--- a/storage/federated/ha_federated.cc
+++ b/storage/federated/ha_federated.cc
@@ -2453,7 +2453,6 @@ int ha_federated::index_init(uint keynr, bool sorted)
{
DBUG_ENTER("ha_federated::index_init");
DBUG_PRINT("info", ("table: '%s' key: %u", table->s->table_name.str, keynr));
- active_index= keynr;
DBUG_RETURN(0);
}
@@ -2589,7 +2588,6 @@ int ha_federated::index_end(void)
{
DBUG_ENTER("ha_federated::index_end");
free_result();
- active_index= MAX_KEY;
DBUG_RETURN(0);
}
diff --git a/storage/heap/ha_heap.cc b/storage/heap/ha_heap.cc
index 2d2c23a2ed6..6bb6936de9d 100644
--- a/storage/heap/ha_heap.cc
+++ b/storage/heap/ha_heap.cc
@@ -183,6 +183,19 @@ void ha_heap::set_keys_for_scanning(void)
}
+int ha_heap::can_continue_handler_scan()
+{
+ int error= 0;
+ if ((file->key_version != file->s->key_version && inited == INDEX) ||
+ (file->file_version != file->s->file_version && inited == RND))
+ {
+ /* Data changed, not safe to do index or rnd scan */
+ error= HA_ERR_RECORD_CHANGED;
+ }
+ return error;
+}
+
+
void ha_heap::update_key_stats()
{
for (uint i= 0; i < table->s->keys; i++)
diff --git a/storage/heap/ha_heap.h b/storage/heap/ha_heap.h
index 22722129f4c..49aa91a6caa 100644
--- a/storage/heap/ha_heap.h
+++ b/storage/heap/ha_heap.h
@@ -1,4 +1,4 @@
-/* Copyright (C) 2000-2006 MySQL AB
+/* Copyright (C) 2000-2006 MySQL AB, 2009-2011 Monty Program 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
@@ -52,6 +52,7 @@ public:
{
return (HA_FAST_KEY_READ | HA_NO_BLOBS | HA_NULL_IN_KEY |
HA_BINLOG_ROW_CAPABLE | HA_BINLOG_STMT_CAPABLE |
+ HA_CAN_SQL_HANDLER |
HA_REC_NOT_IN_SEQ | HA_CAN_INSERT_DELAYED | HA_NO_TRANSACTIONS |
HA_HAS_RECORDS | HA_STATS_RECORDS_IS_EXACT);
}
@@ -93,6 +94,7 @@ public:
int rnd_next(uchar *buf);
int rnd_pos(uchar * buf, uchar *pos);
void position(const uchar *record);
+ int can_continue_handler_scan();
int info(uint);
int extra(enum ha_extra_function operation);
int reset();
diff --git a/storage/heap/hp_clear.c b/storage/heap/hp_clear.c
index babfcbd6f41..b999f1ff5e8 100644
--- a/storage/heap/hp_clear.c
+++ b/storage/heap/hp_clear.c
@@ -40,6 +40,8 @@ void hp_clear(HP_SHARE *info)
info->blength=1;
info->changed=0;
info->del_link=0;
+ info->key_version++;
+ info->file_version++;
DBUG_VOID_RETURN;
}
diff --git a/storage/heap/hp_delete.c b/storage/heap/hp_delete.c
index 9e9e28da335..1af9cf26f8c 100644
--- a/storage/heap/hp_delete.c
+++ b/storage/heap/hp_delete.c
@@ -47,6 +47,7 @@ int heap_delete(HP_INFO *info, const uchar *record)
share->del_link=pos;
pos[share->reclength]=0; /* Record deleted */
share->deleted++;
+ share->key_version++;
info->current_hash_ptr=0;
#if !defined(DBUG_OFF) && defined(EXTRA_HEAP_DEBUG)
DBUG_EXECUTE("check_heap",heap_check_heap(info, 0););
diff --git a/storage/heap/hp_rfirst.c b/storage/heap/hp_rfirst.c
index d0d2ec9b506..8e562983b02 100644
--- a/storage/heap/hp_rfirst.c
+++ b/storage/heap/hp_rfirst.c
@@ -24,6 +24,8 @@ int heap_rfirst(HP_INFO *info, uchar *record, int inx)
DBUG_ENTER("heap_rfirst");
info->lastinx= inx;
+ info->key_version= info->s->key_version;
+
if (keyinfo->algorithm == HA_KEY_ALG_BTREE)
{
uchar *pos;
@@ -57,15 +59,8 @@ int heap_rfirst(HP_INFO *info, uchar *record, int inx)
}
else
{
- if (!(info->s->records))
- {
- my_errno=HA_ERR_END_OF_FILE;
- DBUG_RETURN(my_errno);
- }
- DBUG_ASSERT(0); /* TODO fix it */
- info->current_record=0;
- info->current_hash_ptr=0;
- info->update=HA_STATE_PREV_FOUND;
- DBUG_RETURN(heap_rnext(info,record));
+ /* We can't scan a non existing key value with hash index */
+ my_errno= HA_ERR_WRONG_COMMAND;
+ DBUG_RETURN(my_errno);
}
}
diff --git a/storage/heap/hp_rkey.c b/storage/heap/hp_rkey.c
index 27d1114770e..c2edf63f6f2 100644
--- a/storage/heap/hp_rkey.c
+++ b/storage/heap/hp_rkey.c
@@ -30,6 +30,7 @@ int heap_rkey(HP_INFO *info, uchar *record, int inx, const uchar *key,
}
info->lastinx= inx;
info->current_record= (ulong) ~0L; /* For heap_rrnd() */
+ info->key_version= info->s->key_version;
if (keyinfo->algorithm == HA_KEY_ALG_BTREE)
{
diff --git a/storage/heap/hp_rlast.c b/storage/heap/hp_rlast.c
index 45ad7c21f49..0710401e5a5 100644
--- a/storage/heap/hp_rlast.c
+++ b/storage/heap/hp_rlast.c
@@ -25,6 +25,7 @@ int heap_rlast(HP_INFO *info, uchar *record, int inx)
DBUG_ENTER("heap_rlast");
info->lastinx= inx;
+ info->key_version= info->s->key_version;
if (keyinfo->algorithm == HA_KEY_ALG_BTREE)
{
uchar *pos;
@@ -47,9 +48,8 @@ int heap_rlast(HP_INFO *info, uchar *record, int inx)
}
else
{
- info->current_ptr=0;
- info->current_hash_ptr=0;
- info->update=HA_STATE_NEXT_FOUND;
- DBUG_RETURN(heap_rprev(info,record));
+ /* We can't scan a non existing key value with hash index */
+ my_errno= HA_ERR_WRONG_COMMAND;
+ DBUG_RETURN(my_errno);
}
}
diff --git a/storage/heap/hp_rnext.c b/storage/heap/hp_rnext.c
index 3d715f4e6d3..7a654850e0e 100644
--- a/storage/heap/hp_rnext.c
+++ b/storage/heap/hp_rnext.c
@@ -70,6 +70,7 @@ int heap_rnext(HP_INFO *info, uchar *record)
custom_arg.keyseg = keyinfo->seg;
custom_arg.key_length = info->lastkey_len;
custom_arg.search_flag = SEARCH_SAME | SEARCH_FIND;
+ info->last_find_flag= HA_READ_KEY_OR_NEXT;
pos = tree_search_key(&keyinfo->rb_tree, info->lastkey, info->parents,
&info->last_pos, info->last_find_flag, &custom_arg);
}
diff --git a/storage/heap/hp_rprev.c b/storage/heap/hp_rprev.c
index 63bfffffba9..1d71c20eef4 100644
--- a/storage/heap/hp_rprev.c
+++ b/storage/heap/hp_rprev.c
@@ -41,6 +41,7 @@ int heap_rprev(HP_INFO *info, uchar *record)
custom_arg.keyseg = keyinfo->seg;
custom_arg.key_length = keyinfo->length;
custom_arg.search_flag = SEARCH_SAME;
+ info->last_find_flag= HA_READ_KEY_OR_PREV;
pos = tree_search_key(&keyinfo->rb_tree, info->lastkey, info->parents,
&info->last_pos, info->last_find_flag, &custom_arg);
}
diff --git a/storage/heap/hp_scan.c b/storage/heap/hp_scan.c
index e8913e92c86..397dd8b54d4 100644
--- a/storage/heap/hp_scan.c
+++ b/storage/heap/hp_scan.c
@@ -31,6 +31,8 @@ int heap_scan_init(register HP_INFO *info)
info->current_record= (ulong) ~0L; /* No current record */
info->update=0;
info->next_block=0;
+ info->key_version= info->s->key_version;
+ info->file_version= info->s->file_version;
DBUG_RETURN(0);
}
diff --git a/storage/heap/hp_update.c b/storage/heap/hp_update.c
index 7f469af3c96..ab831382325 100644
--- a/storage/heap/hp_update.c
+++ b/storage/heap/hp_update.c
@@ -21,7 +21,7 @@ int heap_update(HP_INFO *info, const uchar *old, const uchar *heap_new)
{
HP_KEYDEF *keydef, *end, *p_lastinx;
uchar *pos;
- my_bool auto_key_changed= 0;
+ my_bool auto_key_changed= 0, key_changed= 0;
HP_SHARE *share= info->s;
DBUG_ENTER("heap_update");
@@ -54,6 +54,8 @@ int heap_update(HP_INFO *info, const uchar *old, const uchar *heap_new)
#endif
if (auto_key_changed)
heap_update_auto_increment(info, heap_new);
+ if (key_changed)
+ share->key_version++;
DBUG_RETURN(0);
err:
diff --git a/storage/heap/hp_write.c b/storage/heap/hp_write.c
index 4e8fa7e3580..bf27503de9b 100644
--- a/storage/heap/hp_write.c
+++ b/storage/heap/hp_write.c
@@ -56,6 +56,7 @@ int heap_write(HP_INFO *info, const uchar *record)
pos[share->reclength]=1; /* Mark record as not deleted */
if (++share->records == share->blength)
share->blength+= share->blength;
+ info->s->key_version++;
info->current_ptr=pos;
info->current_hash_ptr=0;
info->update|=HA_STATE_AKTIV;
diff --git a/storage/ibmdb2i/db2i_ioBuffers.h b/storage/ibmdb2i/db2i_ioBuffers.h
index 350d854f055..8fb815ba3be 100644
--- a/storage/ibmdb2i/db2i_ioBuffers.h
+++ b/storage/ibmdb2i/db2i_ioBuffers.h
@@ -290,7 +290,7 @@ class IOAsyncReadBuffer : public IOReadBuffer
Return a pointer to the next row in the table, where "next" is defined
by the orientation.
- @param orientaton
+ @param orientation
@param[out] rrn The relative record number of the row returned. Not reliable
if NULL is returned by this function.
diff --git a/storage/innodb_plugin/trx/trx0sys.c b/storage/innodb_plugin/trx/trx0sys.c
index 6eb356947cc..48963bc7e45 100644
--- a/storage/innodb_plugin/trx/trx0sys.c
+++ b/storage/innodb_plugin/trx/trx0sys.c
@@ -1343,7 +1343,7 @@ trx_sys_print_mysql_binlog_offset_from_page(
/* THESE ARE COPIED FROM NON-HOTBACKUP PART OF THE INNODB SOURCE TREE
- (This code duplicaton should be fixed at some point!)
+ (This code duplication should be fixed at some point!)
*/
#define TRX_SYS_SPACE 0 /* the SYSTEM tablespace */
diff --git a/storage/maria/ha_maria.cc b/storage/maria/ha_maria.cc
index 406def92808..2cf981d5076 100644
--- a/storage/maria/ha_maria.cc
+++ b/storage/maria/ha_maria.cc
@@ -228,7 +228,7 @@ static MYSQL_SYSVAR_ULONGLONG(pagecache_buffer_size, pagecache_buffer_size,
"The size of the buffer used for index blocks for Aria tables. "
"Increase this to get better index handling (for all reads and "
"multiple writes) to as much as you can afford.", 0, 0,
- KEY_CACHE_SIZE, 0, ~(ulong) 0, 1);
+ KEY_CACHE_SIZE, 8192*16L, ~(ulong) 0, 1);
static MYSQL_SYSVAR_ULONG(pagecache_division_limit, pagecache_division_limit,
PLUGIN_VAR_RQCMDARG,
@@ -826,7 +826,7 @@ int_table_flags(HA_NULL_IN_KEY | HA_CAN_FULLTEXT | HA_CAN_SQL_HANDLER |
HA_BINLOG_ROW_CAPABLE | HA_BINLOG_STMT_CAPABLE |
HA_DUPLICATE_POS | HA_CAN_INDEX_BLOBS | HA_AUTO_PART_KEY |
HA_FILE_BASED | HA_CAN_GEOMETRY | CANNOT_ROLLBACK_FLAG |
- HA_CAN_BIT_FIELD | HA_CAN_RTREEKEYS |
+ HA_CAN_BIT_FIELD | HA_CAN_RTREEKEYS | HA_CAN_VIRTUAL_COLUMNS |
HA_HAS_RECORDS | HA_STATS_RECORDS_IS_EXACT),
can_enable_indexes(1), bulk_insert_single_undo(BULK_INSERT_NONE)
{}
@@ -1078,8 +1078,6 @@ int ha_maria::close(void)
int ha_maria::write_row(uchar * buf)
{
- ha_statistic_increment(&SSV::ha_write_count);
-
/* If we have a timestamp column, update it to the current time */
if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_INSERT)
table->timestamp_field->set_time();
@@ -2086,6 +2084,7 @@ bool ha_maria::check_and_repair(THD *thd)
DBUG_ENTER("ha_maria::check_and_repair");
check_opt.init();
+ check_opt.flags= T_MEDIUM | T_AUTO_REPAIR;
error= 1;
if ((file->s->state.changed &
@@ -2106,7 +2105,6 @@ bool ha_maria::check_and_repair(THD *thd)
DBUG_RETURN(error);
error= 0;
- check_opt.flags= T_MEDIUM | T_AUTO_REPAIR;
// Don't use quick if deleted rows
if (!file->state->del && (maria_recover_options & HA_RECOVER_QUICK))
check_opt.flags |= T_QUICK;
@@ -2157,7 +2155,6 @@ bool ha_maria::is_crashed() const
int ha_maria::update_row(const uchar * old_data, uchar * new_data)
{
CHECK_UNTIL_WE_FULLY_IMPLEMENTED_VERSIONING("UPDATE in WRITE CONCURRENT");
- ha_statistic_increment(&SSV::ha_update_count);
if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_UPDATE)
table->timestamp_field->set_time();
return maria_update(file, old_data, new_data);
@@ -2167,7 +2164,6 @@ int ha_maria::update_row(const uchar * old_data, uchar * new_data)
int ha_maria::delete_row(const uchar * buf)
{
CHECK_UNTIL_WE_FULLY_IMPLEMENTED_VERSIONING("DELETE in WRITE CONCURRENT");
- ha_statistic_increment(&SSV::ha_delete_count);
return maria_delete(file, buf);
}
@@ -2191,7 +2187,6 @@ int ha_maria::index_read_map(uchar * buf, const uchar * key,
enum ha_rkey_function find_flag)
{
DBUG_ASSERT(inited == INDEX);
- ha_statistic_increment(&SSV::ha_read_key_count);
int error= maria_rkey(file, buf, active_index, key, keypart_map, find_flag);
table->status= error ? STATUS_NOT_FOUND : 0;
return error;
@@ -2202,7 +2197,6 @@ int ha_maria::index_read_idx_map(uchar * buf, uint index, const uchar * key,
key_part_map keypart_map,
enum ha_rkey_function find_flag)
{
- ha_statistic_increment(&SSV::ha_read_key_count);
int error= maria_rkey(file, buf, index, key, keypart_map, find_flag);
table->status= error ? STATUS_NOT_FOUND : 0;
return error;
@@ -2214,7 +2208,6 @@ int ha_maria::index_read_last_map(uchar * buf, const uchar * key,
{
DBUG_ENTER("ha_maria::index_read_last_map");
DBUG_ASSERT(inited == INDEX);
- ha_statistic_increment(&SSV::ha_read_key_count);
int error= maria_rkey(file, buf, active_index, key, keypart_map,
HA_READ_PREFIX_LAST);
table->status= error ? STATUS_NOT_FOUND : 0;
@@ -2225,7 +2218,6 @@ int ha_maria::index_read_last_map(uchar * buf, const uchar * key,
int ha_maria::index_next(uchar * buf)
{
DBUG_ASSERT(inited == INDEX);
- ha_statistic_increment(&SSV::ha_read_next_count);
int error= maria_rnext(file, buf, active_index);
table->status= error ? STATUS_NOT_FOUND : 0;
return error;
@@ -2235,7 +2227,6 @@ int ha_maria::index_next(uchar * buf)
int ha_maria::index_prev(uchar * buf)
{
DBUG_ASSERT(inited == INDEX);
- ha_statistic_increment(&SSV::ha_read_prev_count);
int error= maria_rprev(file, buf, active_index);
table->status= error ? STATUS_NOT_FOUND : 0;
return error;
@@ -2245,7 +2236,6 @@ int ha_maria::index_prev(uchar * buf)
int ha_maria::index_first(uchar * buf)
{
DBUG_ASSERT(inited == INDEX);
- ha_statistic_increment(&SSV::ha_read_first_count);
int error= maria_rfirst(file, buf, active_index);
table->status= error ? STATUS_NOT_FOUND : 0;
return error;
@@ -2255,7 +2245,6 @@ int ha_maria::index_first(uchar * buf)
int ha_maria::index_last(uchar * buf)
{
DBUG_ASSERT(inited == INDEX);
- ha_statistic_increment(&SSV::ha_read_last_count);
int error= maria_rlast(file, buf, active_index);
table->status= error ? STATUS_NOT_FOUND : 0;
return error;
@@ -2268,7 +2257,6 @@ int ha_maria::index_next_same(uchar * buf,
{
int error;
DBUG_ASSERT(inited == INDEX);
- ha_statistic_increment(&SSV::ha_read_next_count);
/*
TODO: Delete this loop in Maria 1.5 as versioning will ensure this never
happens
@@ -2320,7 +2308,6 @@ int ha_maria::rnd_end()
int ha_maria::rnd_next(uchar *buf)
{
- ha_statistic_increment(&SSV::ha_read_rnd_next_count);
int error= maria_scan(file, buf);
table->status= error ? STATUS_NOT_FOUND : 0;
return error;
@@ -2342,7 +2329,6 @@ int ha_maria::restart_rnd_next(uchar *buf)
int ha_maria::rnd_pos(uchar *buf, uchar *pos)
{
- ha_statistic_increment(&SSV::ha_read_rnd_count);
int error= maria_rrnd(file, buf, my_get_ptr(pos, ref_length));
table->status= error ? STATUS_NOT_FOUND : 0;
return error;
@@ -3367,6 +3353,9 @@ static int ha_maria_init(void *p)
maria_hton->panic= maria_hton_panic;
maria_hton->commit= maria_commit;
maria_hton->rollback= maria_rollback;
+#ifdef MARIA_CANNOT_ROLLBACK
+ maria_hton->commit= 0;
+#endif
maria_hton->flush_logs= maria_flush_logs;
maria_hton->show_status= maria_show_status;
/* TODO: decide if we support Maria being used for log tables */
diff --git a/storage/maria/ha_maria.h b/storage/maria/ha_maria.h
index 0433134275c..5e7b068d01c 100644
--- a/storage/maria/ha_maria.h
+++ b/storage/maria/ha_maria.h
@@ -157,7 +157,6 @@ public:
int assign_to_keycache(THD * thd, HA_CHECK_OPT * check_opt);
int preload_keys(THD * thd, HA_CHECK_OPT * check_opt);
bool check_if_incompatible_data(HA_CREATE_INFO * info, uint table_changes);
- bool check_if_supported_virtual_columns(void) { return TRUE;}
#ifdef HAVE_REPLICATION
int dump(THD * thd, int fd);
int net_read_dump(NET * net);
diff --git a/storage/maria/ma_bitmap.c b/storage/maria/ma_bitmap.c
index 7e68437de2b..bc83f6ea0e9 100644
--- a/storage/maria/ma_bitmap.c
+++ b/storage/maria/ma_bitmap.c
@@ -399,7 +399,8 @@ my_bool _ma_bitmap_flush_all(MARIA_SHARE *share)
become false, wake them up.
*/
DBUG_PRINT("info", ("bitmap flusher waking up others"));
- pthread_cond_broadcast(&bitmap->bitmap_cond);
+ if (bitmap->flush_all_requested)
+ pthread_cond_broadcast(&bitmap->bitmap_cond);
}
pthread_mutex_unlock(&bitmap->bitmap_lock);
DBUG_RETURN(res);
@@ -465,7 +466,8 @@ void _ma_bitmap_unlock(MARIA_SHARE *share)
bitmap->flush_all_requested--;
bitmap->non_flushable= 0;
pthread_mutex_unlock(&bitmap->bitmap_lock);
- pthread_cond_broadcast(&bitmap->bitmap_cond);
+ if (bitmap->flush_all_requested > 0)
+ pthread_cond_broadcast(&bitmap->bitmap_cond);
DBUG_VOID_RETURN;
}
diff --git a/storage/maria/ma_blockrec.c b/storage/maria/ma_blockrec.c
index fd02e2ac0ec..a2faeb6e7fa 100644
--- a/storage/maria/ma_blockrec.c
+++ b/storage/maria/ma_blockrec.c
@@ -2506,7 +2506,7 @@ static my_bool free_full_page_range(MARIA_HA *info, pgcache_page_no_t page,
}
if (delete_count &&
pagecache_delete_pages(share->pagecache, &info->dfile,
- page, delete_count, PAGECACHE_LOCK_WRITE, 0))
+ page, delete_count, PAGECACHE_LOCK_WRITE, 1))
res= 1;
if (share->now_transactional)
@@ -2816,7 +2816,6 @@ static my_bool write_block_record(MARIA_HA *info,
DBUG_PRINT("info", ("Used head length on page: %u header_length: %u",
head_length,
(uint) (flag & ROW_FLAG_TRANSID ? TRANSID_SIZE : 0)));
- DBUG_ASSERT(data <= end_of_data);
if (head_length < share->base.min_block_length)
{
/* Extend row to be of size min_block_length */
@@ -2825,6 +2824,7 @@ static my_bool write_block_record(MARIA_HA *info,
data+= diff_length;
head_length= share->base.min_block_length;
}
+ DBUG_ASSERT(data <= end_of_data);
/*
If this is a redo entry (ie, undo_lsn != LSN_ERROR) then we should have
written exactly head_length bytes (same as original record).
@@ -3492,7 +3492,9 @@ static my_bool allocate_and_write_block_record(MARIA_HA *info,
/* page will be pinned & locked by get_head_or_tail_page */
if (get_head_or_tail_page(info, blocks->block, info->buff,
- row->space_on_head_page, HEAD_PAGE,
+ max(row->space_on_head_page,
+ info->s->base.min_block_length),
+ HEAD_PAGE,
PAGECACHE_LOCK_WRITE, &row_pos))
goto err;
row->lastpos= ma_recordpos(blocks->block->page, row_pos.rownr);
@@ -4179,6 +4181,13 @@ static my_bool delete_head_or_tail(MARIA_HA *info,
log_data, NULL))
DBUG_RETURN(1);
}
+ /*
+ Mark that this page must be written to disk by page cache, even
+ if we could call pagecache_delete() on it.
+ This is needed to ensure that repair finds the empty page on disk
+ and not old data.
+ */
+ pagecache_set_write_on_delete_by_link(page_link.link);
DBUG_ASSERT(empty_space >= share->bitmap.sizes[0]);
}
@@ -4932,7 +4941,7 @@ int _ma_read_block_record2(MARIA_HA *info, uchar *record,
goto err;
}
#ifdef EXTRA_DEBUG
- if (share->calc_checksum)
+ if (share->calc_checksum && !info->in_check_table)
{
/* Esnure that row checksum is correct */
DBUG_ASSERT(((share->calc_checksum)(info, record) & 255) ==
@@ -6485,7 +6494,13 @@ err:
@param info Maria handler
@param header Header (without FILEID)
- @note It marks the pages free in the bitmap
+ Mark the pages free in the bitmap.
+
+ We have to check against _ma_redo_not_needed_for_page()
+ to guard against the case where we first clear a block and after
+ that insert new data into the blocks. If we would unconditionally
+ clear the bitmap here, future changes would be ignored for the page
+ if it's not in the dirty list (ie, it would be flushed).
@return Operation status
@retval 0 OK
@@ -6494,19 +6509,25 @@ err:
uint _ma_apply_redo_free_blocks(MARIA_HA *info,
LSN lsn __attribute__((unused)),
+ LSN redo_lsn,
const uchar *header)
{
MARIA_SHARE *share= info->s;
uint ranges;
+ uint16 sid;
DBUG_ENTER("_ma_apply_redo_free_blocks");
share->state.changed|= (STATE_CHANGED | STATE_NOT_ZEROFILLED |
STATE_NOT_MOVABLE);
+ sid= fileid_korr(header);
+ header+= FILEID_STORE_SIZE;
ranges= pagerange_korr(header);
header+= PAGERANGE_STORE_SIZE;
DBUG_ASSERT(ranges > 0);
+ /** @todo leave bitmap lock to the bitmap code... */
+ pthread_mutex_lock(&share->bitmap.bitmap_lock);
while (ranges--)
{
my_bool res;
@@ -6523,18 +6544,22 @@ uint _ma_apply_redo_free_blocks(MARIA_HA *info,
DBUG_PRINT("info", ("page: %lu pages: %u", (long) page, page_range));
- /** @todo leave bitmap lock to the bitmap code... */
- pthread_mutex_lock(&share->bitmap.bitmap_lock);
- res= _ma_bitmap_reset_full_page_bits(info, &share->bitmap, start_page,
- page_range);
- pthread_mutex_unlock(&share->bitmap.bitmap_lock);
- if (res)
+ for ( ; page_range-- ; start_page++)
{
- _ma_mark_file_crashed(share);
- DBUG_ASSERT(0);
- DBUG_RETURN(res);
+ if (_ma_redo_not_needed_for_page(sid, redo_lsn, start_page, FALSE))
+ continue;
+ res= _ma_bitmap_reset_full_page_bits(info, &share->bitmap, start_page,
+ 1);
+ if (res)
+ {
+ pthread_mutex_unlock(&share->bitmap.bitmap_lock);
+ _ma_mark_file_crashed(share);
+ DBUG_ASSERT(0);
+ DBUG_RETURN(res);
+ }
}
}
+ pthread_mutex_unlock(&share->bitmap.bitmap_lock);
DBUG_RETURN(0);
}
@@ -6687,21 +6712,23 @@ uint _ma_apply_redo_insert_row_blobs(MARIA_HA *info,
uint page_range;
pgcache_page_no_t page, start_page;
uchar *buff;
+ uint data_on_page= data_size;
start_page= page= page_korr(header);
header+= PAGE_STORE_SIZE;
page_range= pagerange_korr(header);
header+= PAGERANGE_STORE_SIZE;
- for (i= page_range; i-- > 0 ; page++)
+ for (i= page_range; i-- > 0 ; page++, data+= data_on_page)
{
MARIA_PINNED_PAGE page_link;
enum pagecache_page_lock unlock_method;
enum pagecache_page_pin unpin_method;
- uint length;
set_if_smaller(first_page2, page);
set_if_bigger(last_page2, page);
+ if (i == 0 && sub_ranges == 0)
+ data_on_page= data_size - empty_space; /* data on last page */
if (_ma_redo_not_needed_for_page(sid, redo_lsn, page, FALSE))
continue;
@@ -6764,7 +6791,7 @@ uint _ma_apply_redo_insert_row_blobs(MARIA_HA *info,
PAGECACHE_LOCK_WRITE_UNLOCK,
PAGECACHE_UNPIN, LSN_IMPOSSIBLE,
LSN_IMPOSSIBLE, 0, FALSE);
- continue;
+ goto fix_bitmap;
}
DBUG_ASSERT((found_page_type == (uchar) BLOB_PAGE) ||
(found_page_type == (uchar) UNALLOCATED_PAGE));
@@ -6780,33 +6807,32 @@ uint _ma_apply_redo_insert_row_blobs(MARIA_HA *info,
lsn_store(buff, lsn);
buff[PAGE_TYPE_OFFSET]= BLOB_PAGE;
- length= data_size;
- if (i == 0 && sub_ranges == 0)
+ if (data_on_page != data_size)
{
/*
Last page may be only partly filled. We zero the rest, like
write_full_pages() does.
*/
- length-= empty_space;
bzero(buff + share->block_size - PAGE_SUFFIX_SIZE - empty_space,
empty_space);
}
- memcpy(buff+ PAGE_TYPE_OFFSET + 1, data, length);
- data+= length;
+ memcpy(buff+ PAGE_TYPE_OFFSET + 1, data, data_on_page);
if (pagecache_write(share->pagecache,
&info->dfile, page, 0,
buff, PAGECACHE_PLAIN_PAGE,
unlock_method, unpin_method,
PAGECACHE_WRITE_DELAY, 0, LSN_IMPOSSIBLE))
goto err;
- }
+
+ fix_bitmap:
/** @todo leave bitmap lock to the bitmap code... */
- pthread_mutex_lock(&share->bitmap.bitmap_lock);
- res= _ma_bitmap_set_full_page_bits(info, &share->bitmap, start_page,
- page_range);
- pthread_mutex_unlock(&share->bitmap.bitmap_lock);
- if (res)
- goto err;
+ pthread_mutex_lock(&share->bitmap.bitmap_lock);
+ res= _ma_bitmap_set_full_page_bits(info, &share->bitmap, page,
+ 1);
+ pthread_mutex_unlock(&share->bitmap.bitmap_lock);
+ if (res)
+ goto err;
+ }
}
}
*first_page= first_page2;
diff --git a/storage/maria/ma_blockrec.h b/storage/maria/ma_blockrec.h
index a5858880dd0..e63af8eed5b 100644
--- a/storage/maria/ma_blockrec.h
+++ b/storage/maria/ma_blockrec.h
@@ -235,7 +235,7 @@ uint _ma_apply_redo_insert_row_head_or_tail(MARIA_HA *info, LSN lsn,
uint _ma_apply_redo_purge_row_head_or_tail(MARIA_HA *info, LSN lsn,
uint page_type,
const uchar *header);
-uint _ma_apply_redo_free_blocks(MARIA_HA *info, LSN lsn,
+uint _ma_apply_redo_free_blocks(MARIA_HA *info, LSN lsn, LSN rec_lsn,
const uchar *header);
uint _ma_apply_redo_free_head_or_tail(MARIA_HA *info, LSN lsn,
const uchar *header);
diff --git a/storage/maria/ma_check.c b/storage/maria/ma_check.c
index b7bca730051..4877f5c6d71 100644
--- a/storage/maria/ma_check.c
+++ b/storage/maria/ma_check.c
@@ -100,6 +100,9 @@ static my_bool _ma_flush_table_files_before_swap(HA_CHECK *param,
static TrID max_trid_in_system(void);
static void _ma_check_print_not_visible_error(HA_CHECK *param, TrID used_trid);
void retry_if_quick(MARIA_SORT_PARAM *param, int error);
+static void print_bitmap_description(MARIA_SHARE *share,
+ pgcache_page_no_t page,
+ uchar *buff);
/* Initialize check param with default values */
@@ -878,6 +881,7 @@ static int chk_index(HA_CHECK *param, MARIA_HA *info, MARIA_KEYDEF *keyinfo,
}
info->last_key.keyinfo= tmp_key.keyinfo= keyinfo;
+ info->lastinx= ~0; /* Safety */
tmp_key.data= tmp_key_buff;
for ( ;; )
{
@@ -1133,6 +1137,7 @@ static int check_keys_in_record(HA_CHECK *param, MARIA_HA *info, int extend,
{
(*keyinfo->make_key)(info, &key, keynr, info->lastkey_buff, record,
start_recpos, 0);
+ info->last_key.keyinfo= key.keyinfo;
if (extend)
{
/* We don't need to lock the key tree here as we don't allow
@@ -1844,6 +1849,8 @@ static int check_block_record(HA_CHECK *param, MARIA_HA *info, int extend,
}
param->used+= block_size;
param->link_used+= block_size;
+ if (param->verbose > 2)
+ print_bitmap_description(share, page, bitmap_buff);
continue;
}
/* Skip pages marked as empty in bitmap */
@@ -2036,6 +2043,8 @@ int maria_chk_data_link(HA_CHECK *param, MARIA_HA *info, my_bool extend)
bzero((char*) param->tmp_key_crc,
share->base.keys * sizeof(param->tmp_key_crc[0]));
+ info->in_check_table= 1; /* Don't assert on checksum errors */
+
switch (share->data_file_type) {
case BLOCK_RECORD:
error= check_block_record(param, info, extend, record);
@@ -2057,6 +2066,8 @@ int maria_chk_data_link(HA_CHECK *param, MARIA_HA *info, my_bool extend)
break;
} /* switch */
+ info->in_check_table= 0;
+
if (error)
goto err;
@@ -2185,12 +2196,17 @@ int maria_chk_data_link(HA_CHECK *param, MARIA_HA *info, my_bool extend)
llstr(param->del_length, llbuff2));
printf("Empty space: %12s Linkdata: %10s\n",
llstr(param->empty, llbuff),llstr(param->link_used, llbuff2));
- if (param->lost)
- printf("Lost space: %12s", llstr(param->lost, llbuff));
- if (param->max_found_trid)
+ if (share->data_file_type == BLOCK_RECORD)
{
- printf("Max trans. id: %11s\n",
- llstr(param->max_found_trid, llbuff));
+ printf("Full pages: %12s Tail count: %12s\n",
+ llstr(param->full_page_count, llbuff),
+ llstr(param->tail_count, llbuff2));
+ printf("Lost space: %12s\n", llstr(param->lost, llbuff));
+ if (param->max_found_trid)
+ {
+ printf("Max trans. id: %11s\n",
+ llstr(param->max_found_trid, llbuff));
+ }
}
}
my_free(record,MYF(0));
@@ -2631,6 +2647,7 @@ int maria_repair(HA_CHECK *param, register MARIA_HA *info,
maria_lock_memory(param); /* Everything is alloced */
+ sort_param.sort_info->info->in_check_table= 1;
/* Re-create all keys, which are set in key_map. */
while (!(error=sort_get_next_record(&sort_param)))
{
@@ -2798,6 +2815,7 @@ err:
VOID(end_io_cache(&sort_info.new_info->rec_cache));
info->opt_flag&= ~(READ_CACHE_USED | WRITE_CACHE_USED);
sort_info.new_info->opt_flag&= ~(READ_CACHE_USED | WRITE_CACHE_USED);
+ sort_param.sort_info->info->in_check_table= 0;
/* this below could fail, shouldn't we detect error? */
if (got_error)
{
@@ -3248,6 +3266,7 @@ static my_bool maria_zerofill_index(HA_CHECK *param, MARIA_HA *info,
uint block_size= share->block_size;
my_bool zero_lsn= (share->base.born_transactional &&
!(param->testflag & T_ZEROFILL_KEEP_LSN));
+ int error= 1;
DBUG_ENTER("maria_zerofill_index");
if (!(param->testflag & T_SILENT))
@@ -3272,7 +3291,7 @@ static my_bool maria_zerofill_index(HA_CHECK *param, MARIA_HA *info,
_ma_check_print_error(param,
"Page %9s: Got error %d when reading index file",
llstr(pos, llbuff), my_errno);
- DBUG_RETURN(1);
+ goto end;
}
if (zero_lsn)
bzero(buff, LSN_SIZE);
@@ -3280,7 +3299,7 @@ static my_bool maria_zerofill_index(HA_CHECK *param, MARIA_HA *info,
if (share->base.born_transactional)
{
uint keynr= _ma_get_keynr(share, buff);
- if (keynr != MARIA_DELETE_KEY_NR)
+ if (keynr < share->base.keys)
{
MARIA_PAGE page;
DBUG_ASSERT(keynr < share->base.keys);
@@ -3292,7 +3311,7 @@ static my_bool maria_zerofill_index(HA_CHECK *param, MARIA_HA *info,
"Page %9s: Got error %d when reading index "
"file",
llstr(pos, llbuff), my_errno);
- DBUG_RETURN(1);
+ goto end;
}
}
}
@@ -3306,10 +3325,13 @@ static my_bool maria_zerofill_index(HA_CHECK *param, MARIA_HA *info,
PAGECACHE_UNPIN, LSN_IMPOSSIBLE,
LSN_IMPOSSIBLE, 1, FALSE);
}
+ error= 0; /* ok */
+
+end:
if (flush_pagecache_blocks(share->pagecache, &share->kfile,
FLUSH_FORCE_WRITE))
DBUG_RETURN(1);
- DBUG_RETURN(0);
+ DBUG_RETURN(error);
}
@@ -4769,7 +4791,7 @@ static int sort_get_next_record(MARIA_SORT_PARAM *sort_param)
DBUG_RETURN(-1);
}
/* Retry only if wrong record, not if disk error */
- if (flag != HA_ERR_WRONG_IN_RECORD)
+ if (flag != HA_ERR_WRONG_IN_RECORD && flag != HA_ERR_WRONG_CRC)
{
retry_if_quick(sort_param, flag);
DBUG_RETURN(flag);
@@ -6463,6 +6485,9 @@ static void change_data_file_descriptor(MARIA_HA *info, File new_file)
static void unuse_data_file_descriptor(MARIA_HA *info)
{
+ (void) flush_pagecache_blocks(info->s->pagecache,
+ &info->s->bitmap.file,
+ FLUSH_IGNORE_CHANGED);
info->dfile.file= info->s->bitmap.file.file= -1;
_ma_bitmap_reset_cache(info->s);
}
@@ -6818,3 +6843,46 @@ void retry_if_quick(MARIA_SORT_PARAM *sort_param, int error)
param->testflag|=T_RETRY_WITHOUT_QUICK;
}
}
+
+/* Print information about bitmap page */
+
+static void print_bitmap_description(MARIA_SHARE *share,
+ pgcache_page_no_t page,
+ uchar *bitmap_data)
+{
+ uchar *pos, *end;
+ MARIA_FILE_BITMAP *bitmap= &share->bitmap;
+ uint count=0, dot_printed= 0;
+ char buff[80], last[80];
+
+ printf("Bitmap page %lu\n", (ulong) page);
+ page++;
+ last[0]=0;
+ for (pos= bitmap_data, end= pos+ bitmap->used_size ; pos < end ; pos+= 6)
+ {
+ ulonglong bits= uint6korr(pos); /* 6 bytes = 6*8/3= 16 patterns */
+ uint i;
+
+ for (i= 0; i < 16 ; i++, bits>>= 3)
+ {
+ if (count > 60)
+ {
+ buff[count]= 0;
+ if (strcmp(buff, last))
+ {
+ memcpy(last, buff, count+1);
+ printf("%8lu: %s\n", (ulong) page - count, buff);
+ dot_printed= 0;
+ }
+ else if (!(dot_printed++))
+ printf("...\n");
+ count= 0;
+ }
+ buff[count++]= '0' + (uint) (bits & 7);
+ page++;
+ }
+ }
+ buff[count]= 0;
+ printf("%8lu: %s\n", (ulong) page - count, buff);
+ fputs("\n", stdout);
+}
diff --git a/storage/maria/ma_extra.c b/storage/maria/ma_extra.c
index a2d40b1c828..2d1ed6d3405 100644
--- a/storage/maria/ma_extra.c
+++ b/storage/maria/ma_extra.c
@@ -50,7 +50,7 @@ int maria_extra(MARIA_HA *info, enum ha_extra_function function,
switch (function) {
case HA_EXTRA_RESET_STATE: /* Reset state (don't free buffers) */
- info->lastinx= 0; /* Use first index as def */
+ info->lastinx= ~0; /* Detect index changes */
info->last_search_keypage= info->cur_row.lastpos= HA_OFFSET_ERROR;
info->page_changed= 1;
/* Next/prev gives first/last */
@@ -553,7 +553,7 @@ int maria_reset(MARIA_HA *info)
#endif
info->opt_flag&= ~(KEY_READ_USED | REMEMBER_OLD_POS);
info->quick_mode= 0;
- info->lastinx= 0; /* Use first index as def */
+ info->lastinx= ~0; /* detect index changes */
info->last_search_keypage= info->cur_row.lastpos= HA_OFFSET_ERROR;
info->page_changed= 1;
info->update= ((info->update & HA_STATE_CHANGED) | HA_STATE_NEXT_FOUND |
diff --git a/storage/maria/ma_ft_boolean_search.c b/storage/maria/ma_ft_boolean_search.c
index 0783f679843..13c3a24f362 100644
--- a/storage/maria/ma_ft_boolean_search.c
+++ b/storage/maria/ma_ft_boolean_search.c
@@ -356,7 +356,8 @@ static int _ft2_search(FTB *ftb, FTB_WORD *ftbw, my_bool init_search)
{
ftbw->key_root=info->s->state.key_root[ftb->keynr];
ftbw->keyinfo=info->s->keyinfo+ftb->keynr;
- key.keyinfo= ftbw->keyinfo;
+ info->last_key.keyinfo= key.keyinfo= ftbw->keyinfo;
+ info->lastinx= ~0; /* Safety */
key.data= ftbw->word;
key.data_length= ftbw->len;
key.ref_length= 0;
@@ -380,7 +381,8 @@ static int _ft2_search(FTB *ftb, FTB_WORD *ftbw, my_bool init_search)
max_docid);
}
- key.keyinfo= ftbw->keyinfo;
+ info->last_key.keyinfo= key.keyinfo= ftbw->keyinfo;
+ info->lastinx= ~0; /* Safety */
key.data= lastkey_buf;
key.data_length= USE_WHOLE_KEY;
key.ref_length= 0;
diff --git a/storage/maria/ma_open.c b/storage/maria/ma_open.c
index 89d41589b83..c679fba7a12 100644
--- a/storage/maria/ma_open.c
+++ b/storage/maria/ma_open.c
@@ -130,6 +130,8 @@ static MARIA_HA *maria_clone_internal(MARIA_SHARE *share, const char *name,
info.s=share;
info.cur_row.lastpos= HA_OFFSET_ERROR;
+ /* Impossible first index to force initialization in _ma_check_index() */
+ info.lastinx= ~0;
info.update= (short) (HA_STATE_NEXT_FOUND+HA_STATE_PREV_FOUND);
info.opt_flag=READ_CHECK_USED;
info.this_unique= (ulong) info.dfile.file; /* Uniq number in process */
diff --git a/storage/maria/ma_pagecache.c b/storage/maria/ma_pagecache.c
index 8105c6f752d..42a42b65b8d 100644
--- a/storage/maria/ma_pagecache.c
+++ b/storage/maria/ma_pagecache.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2000-2008 MySQL AB
+/* Copyright (C) 2000-2008 MySQL AB, 2008-2011 Monty Program 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
@@ -158,6 +158,7 @@ struct st_pagecache_hash_link
#define PCBLOCK_IN_FLUSH 16 /* block is in flush operation */
#define PCBLOCK_CHANGED 32 /* block buffer contains a dirty page */
#define PCBLOCK_DIRECT_W 64 /* possible direct write to the block */
+#define PCBLOCK_DEL_WRITE 128 /* should be written on delete */
/* page status, returned by find_block */
#define PAGE_READ 0
@@ -759,6 +760,8 @@ ulong init_pagecache(PAGECACHE *pagecache, size_t use_mem,
{
if (blocks < 8)
{
+ my_message(ENOMEM, "Not enough memory to allocate 8 pagecache pages",
+ MYF(0));
my_errno= ENOMEM;
goto err;
}
@@ -1215,7 +1218,7 @@ static void link_to_file_list(PAGECACHE *pagecache,
link_changed(block, &pagecache->file_blocks[FILE_HASH(*file)]);
if (block->status & PCBLOCK_CHANGED)
{
- block->status&= ~PCBLOCK_CHANGED;
+ block->status&= ~(PCBLOCK_CHANGED | PCBLOCK_DEL_WRITE);
block->rec_lsn= LSN_MAX;
pagecache->blocks_changed--;
pagecache->global_blocks_changed--;
@@ -3473,6 +3476,31 @@ no_key_cache: /* Key cache is not used */
/*
+ @brief Set/reset flag that page always should be flushed on delete
+
+ @param pagecache pointer to a page cache data structure
+ @param link direct link to page (returned by read or write)
+ @param write write on delete flag value
+
+*/
+
+void pagecache_set_write_on_delete_by_link(PAGECACHE_BLOCK_LINK *block)
+{
+ DBUG_ENTER("pagecache_set_write_on_delete_by_link");
+ DBUG_PRINT("enter", ("fd: %d block 0x%lx %d -> TRUE",
+ block->hash_link->file.file,
+ (ulong) block,
+ (int) block->status & PCBLOCK_DEL_WRITE));
+ DBUG_ASSERT(block->pins); /* should be pinned */
+ DBUG_ASSERT(block->wlocks); /* should be write locked */
+
+ block->status|= PCBLOCK_DEL_WRITE;
+
+ DBUG_VOID_RETURN;
+}
+
+
+/*
@brief Delete page from the buffer (common part for link and file/page)
@param pagecache pointer to a page cache data structure
@@ -3501,6 +3529,7 @@ static my_bool pagecache_delete_internal(PAGECACHE *pagecache,
}
if (block->status & PCBLOCK_CHANGED)
{
+ flush= (flush || (block->status & PCBLOCK_DEL_WRITE));
if (flush)
{
/* The block contains a dirty page - push it out of the cache */
@@ -4187,6 +4216,7 @@ static void free_block(PAGECACHE *pagecache, PAGECACHE_BLOCK_LINK *block)
DBUG_ASSERT(block->rlocks == 0);
DBUG_ASSERT(block->rlocks_queue == 0);
DBUG_ASSERT(block->pins == 0);
+ DBUG_ASSERT((block->status & ~(PCBLOCK_ERROR | PCBLOCK_READ | PCBLOCK_IN_FLUSH | PCBLOCK_CHANGED | PCBLOCK_REASSIGNED | PCBLOCK_DEL_WRITE)) == 0);
block->status= 0;
#ifndef DBUG_OFF
block->type= PAGECACHE_EMPTY_PAGE;
@@ -4515,6 +4545,7 @@ static int flush_pagecache_blocks_int(PAGECACHE *pagecache,
KEYCACHE_DBUG_ASSERT(count<= pagecache->blocks_used);
}
}
+ count++; /* Allocate one extra for easy end-of-buffer test */
/* Allocate a new buffer only if its bigger than the one we have */
if (count > FLUSH_CACHE &&
!(cache=
@@ -4552,22 +4583,24 @@ restart:
DBUG_ASSERT(filter_res == FLUSH_FILTER_OK);
}
{
+ DBUG_ASSERT(!(block->status & PCBLOCK_IN_FLUSH));
/*
- Mark the block with BLOCK_IN_FLUSH in order not to let
- other threads to use it for new pages and interfere with
- our sequence of flushing dirty file pages
+ We care only for the blocks for which flushing was not
+ initiated by other threads as a result of page swapping
*/
- block->status|= PCBLOCK_IN_FLUSH;
-
if (! (block->status & PCBLOCK_IN_SWITCH))
{
- /*
- We care only for the blocks for which flushing was not
- initiated by other threads as a result of page swapping
+ /*
+ Mark the block with BLOCK_IN_FLUSH in order not to let
+ other threads to use it for new pages and interfere with
+ our sequence of flushing dirty file pages
*/
+ block->status|= PCBLOCK_IN_FLUSH;
+
reg_requests(pagecache, block, 1);
if (type != FLUSH_IGNORE_CHANGED)
{
+ *pos++= block;
/* It's not a temporary file */
if (pos == end)
{
@@ -4587,7 +4620,6 @@ restart:
*/
goto restart;
}
- *pos++= block;
}
else
{
diff --git a/storage/maria/ma_pagecache.h b/storage/maria/ma_pagecache.h
index 821728ef374..475729bd46d 100644
--- a/storage/maria/ma_pagecache.h
+++ b/storage/maria/ma_pagecache.h
@@ -251,6 +251,7 @@ extern void pagecache_unpin(PAGECACHE *pagecache,
extern void pagecache_unpin_by_link(PAGECACHE *pagecache,
PAGECACHE_BLOCK_LINK *link,
LSN lsn);
+extern void pagecache_set_write_on_delete_by_link(PAGECACHE_BLOCK_LINK *block);
/* Results of flush operation (bit field in fact) */
diff --git a/storage/maria/ma_recovery.c b/storage/maria/ma_recovery.c
index 7a7286e26f9..d2c7bf3d381 100644
--- a/storage/maria/ma_recovery.c
+++ b/storage/maria/ma_recovery.c
@@ -28,6 +28,7 @@
#include "trnman.h"
#include "ma_key_recover.h"
#include "ma_recovery_util.h"
+#include "hash.h"
struct st_trn_for_recovery /* used only in the REDO phase */
{
@@ -58,6 +59,7 @@ static ulonglong now; /**< for tracking execution time of phases */
static int (*save_error_handler_hook)(uint, const char *,myf);
static uint recovery_warnings; /**< count of warnings */
static uint recovery_found_crashed_tables;
+HASH tables_to_redo; /* For maria_read_log */
#define prototype_redo_exec_hook(R) \
static int exec_REDO_LOGREC_ ## R(const TRANSLOG_HEADER_BUFFER *rec)
@@ -184,6 +186,21 @@ static void print_preamble()
}
+static my_bool table_is_part_of_recovery_set(LEX_STRING *file_name)
+{
+ uint offset =0;
+ if (!tables_to_redo.records)
+ return 1; /* Default, recover table */
+
+ /* Skip base directory */
+ if (file_name->str[0] == '.' &&
+ (file_name->str[1] == '/' || file_name->str[1] == '\\'))
+ offset= 2;
+ /* Only recover if table is in hash */
+ return my_hash_search(&tables_to_redo, (uchar*) file_name->str + offset,
+ file_name->length - offset) != 0;
+}
+
/**
@brief Recovers from the last checkpoint.
@@ -1643,8 +1660,8 @@ prototype_redo_exec_hook(REDO_FREE_BLOCKS)
}
buff= log_record_buffer.str;
- if (_ma_apply_redo_free_blocks(info, current_group_end_lsn,
- buff + FILEID_STORE_SIZE))
+ if (_ma_apply_redo_free_blocks(info, current_group_end_lsn, rec->lsn,
+ buff))
goto end;
error= 0;
end:
@@ -3015,10 +3032,11 @@ static MARIA_HA *get_MARIA_HA_from_REDO_record(const
page= page_korr(rec->header + FILEID_STORE_SIZE);
llstr(page, llbuf);
break;
+ case LOGREC_REDO_FREE_BLOCKS:
/*
- For REDO_FREE_BLOCKS, no need to look at dirty pages list: it does not
- read data pages, only reads/modifies bitmap page(s) which is cheap.
+ We are checking against the dirty pages in _ma_apply_redo_free_blocks()
*/
+ break;
default:
break;
}
@@ -3036,6 +3054,12 @@ static MARIA_HA *get_MARIA_HA_from_REDO_record(const
share= info->s;
tprint(tracef, ", '%s'", share->open_file_name.str);
DBUG_ASSERT(in_redo_phase);
+ if (!table_is_part_of_recovery_set(&share->open_file_name))
+ {
+ tprint(tracef, ", skipped by user\n");
+ return NULL;
+ }
+
if (cmp_translog_addr(rec->lsn, share->lsn_of_file_id) <= 0)
{
/*
@@ -3069,7 +3093,6 @@ static MARIA_HA *get_MARIA_HA_from_REDO_record(const
REDO_INSERT_ROW_BLOBS will consult list by itself, as it covers several
pages.
*/
- tprint(tracef, " page %s", llbuf);
if (_ma_redo_not_needed_for_page(sid, rec->lsn, page,
index_page_redo_entry))
return NULL;
@@ -3106,6 +3129,13 @@ static MARIA_HA *get_MARIA_HA_from_UNDO_record(const
}
share= info->s;
tprint(tracef, ", '%s'", share->open_file_name.str);
+
+ if (!table_is_part_of_recovery_set(&share->open_file_name))
+ {
+ tprint(tracef, ", skipped by user\n");
+ return NULL;
+ }
+
if (cmp_translog_addr(rec->lsn, share->lsn_of_file_id) <= 0)
{
tprint(tracef, ", table's LOGREC_FILE_ID has LSN (%lu,0x%lx) more recent"
diff --git a/storage/maria/ma_recovery.h b/storage/maria/ma_recovery.h
index 0bfcdd17d39..5b22c4fd9b2 100644
--- a/storage/maria/ma_recovery.h
+++ b/storage/maria/ma_recovery.h
@@ -30,4 +30,6 @@ int maria_apply_log(LSN lsn, LSN lsn_end, enum maria_apply_log_way apply,
FILE *trace_file,
my_bool execute_undo_phase, my_bool skip_DDLs,
my_bool take_checkpoints, uint *warnings_count);
+/* Table of tables to recover */
+extern HASH tables_to_redo;
C_MODE_END
diff --git a/storage/maria/ma_recovery_util.c b/storage/maria/ma_recovery_util.c
index 19e61daf4ef..ed9d8efc81b 100644
--- a/storage/maria/ma_recovery_util.c
+++ b/storage/maria/ma_recovery_util.c
@@ -129,16 +129,20 @@ my_bool _ma_redo_not_needed_for_page(uint16 shortid, LSN lsn,
Next 2 bytes: table's short id
Next 5 bytes: page number
*/
+ char llbuf[22];
uint64 file_and_page_id=
(((uint64)((index << 16) | shortid)) << 40) | page;
struct st_dirty_page *dirty_page= (struct st_dirty_page *)
hash_search(&all_dirty_pages,
(uchar *)&file_and_page_id, sizeof(file_and_page_id));
- DBUG_PRINT("info", ("in dirty pages list: %d", dirty_page != NULL));
+ DBUG_PRINT("info", ("page %lld in dirty pages list: %d",
+ (ulonglong) page,
+ dirty_page != NULL));
if ((dirty_page == NULL) ||
cmp_translog_addr(lsn, dirty_page->rec_lsn) < 0)
{
- tprint(tracef, ", ignoring because of dirty_pages list\n");
+ tprint(tracef, ", ignoring page %s because of dirty_pages list\n",
+ llstr((ulonglong) page, llbuf));
return TRUE;
}
}
diff --git a/storage/maria/ma_rkey.c b/storage/maria/ma_rkey.c
index 415f20cc631..4b0dde1860a 100644
--- a/storage/maria/ma_rkey.c
+++ b/storage/maria/ma_rkey.c
@@ -44,7 +44,7 @@ int maria_rkey(MARIA_HA *info, uchar *buf, int inx, const uchar *key_data,
info->update&= (HA_STATE_CHANGED | HA_STATE_ROW_CHANGED);
info->last_key_func= search_flag;
- keyinfo= share->keyinfo + inx;
+ keyinfo= info->last_key.keyinfo;
key_buff= info->lastkey_buff+info->s->base.max_key_length;
diff --git a/storage/maria/ma_rnext.c b/storage/maria/ma_rnext.c
index 2fd8bf4e603..e5ee84b61c7 100644
--- a/storage/maria/ma_rnext.c
+++ b/storage/maria/ma_rnext.c
@@ -31,6 +31,7 @@ int maria_rnext(MARIA_HA *info, uchar *buf, int inx)
MARIA_SHARE *share= info->s;
MARIA_KEYDEF *keyinfo;
ICP_RESULT icp_res= ICP_MATCH;
+ uint update_mask= HA_STATE_NEXT_FOUND;
DBUG_ENTER("maria_rnext");
if ((inx = _ma_check_index(info,inx)) < 0)
@@ -62,6 +63,20 @@ int maria_rnext(MARIA_HA *info, uchar *buf, int inx)
error= _ma_search_first(info, keyinfo, share->state.key_root[inx]);
break;
}
+ /*
+ "search first" failed. This means we have no pivot for
+ "search next", or in other words MI_INFO::lastkey is
+ likely uninitialized.
+
+ Normally SQL layer would never request "search next" if
+ "search first" failed. But HANDLER may do anything.
+
+ As mi_rnext() without preceeding mi_rkey()/mi_rfirst()
+ equals to mi_rfirst(), we must restore original state
+ as if failing mi_rfirst() was not called.
+ */
+ if (error)
+ update_mask|= HA_STATE_PREV_FOUND;
}
else
{
@@ -118,7 +133,7 @@ int maria_rnext(MARIA_HA *info, uchar *buf, int inx)
/* Don't clear if database-changed */
info->update&= (HA_STATE_CHANGED | HA_STATE_ROW_CHANGED);
- info->update|= HA_STATE_NEXT_FOUND;
+ info->update|= update_mask;
if (error || icp_res != ICP_MATCH)
{
diff --git a/storage/maria/ma_rsame.c b/storage/maria/ma_rsame.c
index 4bdbfd526ba..a5953cd09cb 100644
--- a/storage/maria/ma_rsame.c
+++ b/storage/maria/ma_rsame.c
@@ -19,7 +19,7 @@
Find current row with read on position or read on key
@notes
- If inx >= 0 find record using key
+ If inx >= 0 find record using key else re-read row on last position
@warning
This function is not row version safe.
@@ -29,6 +29,7 @@
@retval 0 Ok
@retval HA_ERR_KEY_NOT_FOUND Row is deleted
@retval HA_ERR_END_OF_FILE End of file
+ @retval HA_ERR_WRONG_INDEX Wrong inx argument
*/
@@ -36,10 +37,10 @@ int maria_rsame(MARIA_HA *info, uchar *record, int inx)
{
DBUG_ENTER("maria_rsame");
- if (inx != -1 && ! maria_is_key_active(info->s->state.key_map, inx))
+ if (inx >= 0 && _ma_check_index(info, inx) < 0)
{
DBUG_PRINT("error", ("wrong index usage"));
- DBUG_RETURN(my_errno=HA_ERR_WRONG_INDEX);
+ DBUG_RETURN(my_errno);
}
if (info->cur_row.lastpos == HA_OFFSET_ERROR ||
info->update & HA_STATE_DELETED)
@@ -55,8 +56,7 @@ int maria_rsame(MARIA_HA *info, uchar *record, int inx)
if (inx >= 0)
{
- MARIA_KEYDEF *keyinfo= info->s->keyinfo + inx;
- info->lastinx= inx;
+ MARIA_KEYDEF *keyinfo= info->last_key.keyinfo;
(*keyinfo->make_key)(info, &info->last_key, (uint) inx,
info->lastkey_buff, record,
info->cur_row.lastpos,
diff --git a/storage/maria/ma_rt_index.c b/storage/maria/ma_rt_index.c
index 62474dbbad8..bd1b9038ccf 100644
--- a/storage/maria/ma_rt_index.c
+++ b/storage/maria/ma_rt_index.c
@@ -134,7 +134,6 @@ static int maria_rtree_find_req(MARIA_HA *info, MARIA_KEYDEF *keyinfo,
tmp_key.data_length= key_data_length;
info->cur_row.lastpos= _ma_row_pos_from_key(&tmp_key);
- info->last_key.keyinfo= keyinfo;
info->last_key.data_length= key_data_length;
info->last_key.ref_length= share->base.rec_reflength;
info->last_key.flag= 0;
diff --git a/storage/maria/ma_search.c b/storage/maria/ma_search.c
index 4ac6dfeb15f..df74fed842a 100644
--- a/storage/maria/ma_search.c
+++ b/storage/maria/ma_search.c
@@ -38,12 +38,18 @@ int _ma_check_index(MARIA_HA *info, int inx)
if (info->lastinx != inx) /* Index changed */
{
info->lastinx = inx;
+ info->last_key.keyinfo= info->s->keyinfo + inx;
+ info->last_key.flag= 0;
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))
+ if ((info->opt_flag & WRITE_CACHE_USED) && flush_io_cache(&info->rec_cache))
+ {
+ if (unlikely(!my_errno))
+ my_errno= HA_ERR_INTERNAL_ERROR; /* Impossible */
return(-1);
+ }
return(inx);
} /* _ma_check_index */
@@ -95,6 +101,7 @@ int _ma_search(register MARIA_HA *info, MARIA_KEY *key, uint32 nextflag,
@note
Position to row is stored in info->lastpos
+ Last used key is stored in info->last_key
@return
@retval 0 ok (key found)
@@ -120,6 +127,7 @@ static int _ma_search_no_save(register MARIA_HA *info, MARIA_KEY *key,
(ulong) (pos / info->s->block_size),
nextflag, (ulong) info->cur_row.lastpos));
DBUG_EXECUTE("key", _ma_print_key(DBUG_FILE, key););
+ DBUG_ASSERT(info->last_key.keyinfo == key->keyinfo);
if (pos == HA_OFFSET_ERROR)
{
@@ -184,7 +192,6 @@ static int _ma_search_no_save(register MARIA_HA *info, MARIA_KEY *key,
}
}
- info->last_key.keyinfo= keyinfo;
if ((nextflag & (SEARCH_SMALLER | SEARCH_LAST)) && flag != 0)
{
uint not_used[2];
@@ -1702,7 +1709,7 @@ int _ma_search_next(register MARIA_HA *info, MARIA_KEY *key,
}
tmp_key.data= lastkey;
- info->last_key.keyinfo= tmp_key.keyinfo= keyinfo;
+ tmp_key.keyinfo= keyinfo;
if (nextflag & SEARCH_BIGGER) /* Next key */
{
@@ -1784,8 +1791,6 @@ int _ma_search_first(MARIA_HA *info, MARIA_KEYDEF *keyinfo,
first_pos= page.buff + share->keypage_header + page.node;
} while ((pos= _ma_kpos(page.node, first_pos)) != HA_OFFSET_ERROR);
- info->last_key.keyinfo= keyinfo;
-
if (!(*keyinfo->get_key)(&info->last_key, page.flag, page.node, &first_pos))
DBUG_RETURN(-1); /* Crashed */
@@ -1836,8 +1841,6 @@ int _ma_search_last(MARIA_HA *info, MARIA_KEYDEF *keyinfo,
end_of_page= page.buff + page.size;
} while ((pos= _ma_kpos(page.node, end_of_page)) != HA_OFFSET_ERROR);
- info->last_key.keyinfo= keyinfo;
-
if (!_ma_get_last_key(&info->last_key, &page, end_of_page))
DBUG_RETURN(-1);
info->cur_row.lastpos= _ma_row_pos_from_key(&info->last_key);
diff --git a/storage/maria/ma_sort.c b/storage/maria/ma_sort.c
index f7f79f90cf0..a36bb980145 100644
--- a/storage/maria/ma_sort.c
+++ b/storage/maria/ma_sort.c
@@ -275,12 +275,13 @@ static ha_rows find_all_keys(MARIA_SORT_PARAM *info, uint keys,
idx=error=0;
sort_keys[0]= (uchar*) (sort_keys+keys);
+ info->sort_info->info->in_check_table= 1;
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 */
+ goto err; /* purecov: inspected */
continue;
}
@@ -289,7 +290,7 @@ static ha_rows find_all_keys(MARIA_SORT_PARAM *info, uint keys,
if (info->write_keys(info,sort_keys,idx-1,
(BUFFPEK *)alloc_dynamic(buffpek),
tempfile))
- DBUG_RETURN(HA_POS_ERROR); /* purecov: inspected */
+ goto err; /* purecov: inspected */
sort_keys[0]=(uchar*) (sort_keys+keys);
memcpy(sort_keys[0],sort_keys[idx-1],(size_t) info->key_length);
@@ -298,18 +299,23 @@ static ha_rows find_all_keys(MARIA_SORT_PARAM *info, uint keys,
sort_keys[idx]=sort_keys[idx-1]+info->key_length;
}
if (error > 0)
- DBUG_RETURN(HA_POS_ERROR); /* Aborted by get_key */ /* purecov: inspected */
+ goto err; /* 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 */
+ goto err; /* purecov: inspected */
*maxbuffer=buffpek->elements-1;
}
else
*maxbuffer=0;
+ info->sort_info->info->in_check_table= 0;
DBUG_RETURN((*maxbuffer)*(keys-1)+idx);
+
+err:
+ info->sort_info->info->in_check_table= 0; /* purecov: inspected */
+ DBUG_RETURN(HA_POS_ERROR); /* purecov: inspected */
} /* find_all_keys */
diff --git a/storage/maria/ma_unique.c b/storage/maria/ma_unique.c
index c7d277486c4..6f92ca1559c 100644
--- a/storage/maria/ma_unique.c
+++ b/storage/maria/ma_unique.c
@@ -34,6 +34,7 @@ my_bool _ma_check_unique(MARIA_HA *info, MARIA_UNIQUEDEF *def, uchar *record,
MARIA_KEYDEF *keyinfo= &info->s->keyinfo[def->key];
uchar *key_buff= info->lastkey_buff2;
MARIA_KEY key;
+ int error= 0;
DBUG_ENTER("_ma_check_unique");
DBUG_PRINT("enter",("unique_hash: %lu", (ulong) unique_hash));
@@ -44,13 +45,19 @@ my_bool _ma_check_unique(MARIA_HA *info, MARIA_UNIQUEDEF *def, uchar *record,
/* The above changed info->lastkey_buff2. Inform maria_rnext_same(). */
info->update&= ~HA_STATE_RNEXT_SAME;
+ /* Setup that unique key is active key */
+ info->last_key.keyinfo= keyinfo;
+
+ /* any key pointer in data is destroyed */
+ info->lastinx= ~0;
+
DBUG_ASSERT(key.data_length == MARIA_UNIQUE_HASH_LENGTH);
if (_ma_search(info, &key, SEARCH_FIND | SEARCH_SAVE_BUFF,
info->s->state.key_root[def->key]))
{
info->page_changed=1; /* Can't optimize read next */
info->cur_row.lastpos= lastpos;
- DBUG_RETURN(0); /* No matching rows */
+ goto end;
}
for (;;)
@@ -64,7 +71,8 @@ my_bool _ma_check_unique(MARIA_HA *info, MARIA_UNIQUEDEF *def, uchar *record,
info->page_changed= 1; /* Can't optimize read next */
info->cur_row.lastpos= lastpos;
DBUG_PRINT("info",("Found duplicate"));
- DBUG_RETURN(1); /* Found identical */
+ error= 1; /* Found identical */
+ goto end;
}
DBUG_ASSERT(info->last_key.data_length == MARIA_UNIQUE_HASH_LENGTH);
if (_ma_search_next(info, &info->last_key, SEARCH_BIGGER,
@@ -73,9 +81,12 @@ my_bool _ma_check_unique(MARIA_HA *info, MARIA_UNIQUEDEF *def, uchar *record,
{
info->page_changed= 1; /* Can't optimize read next */
info->cur_row.lastpos= lastpos;
- DBUG_RETURN(0); /* end of tree */
+ break; /* end of tree */
}
}
+
+end:
+ DBUG_RETURN(error);
}
diff --git a/storage/maria/maria_def.h b/storage/maria/maria_def.h
index beb286c7d4b..dd190e4e4ec 100644
--- a/storage/maria/maria_def.h
+++ b/storage/maria/maria_def.h
@@ -574,6 +574,7 @@ struct st_maria_handler
my_bool was_locked; /* Was locked in panic */
my_bool append_insert_at_end; /* Set if concurrent insert */
my_bool quick_mode;
+ my_bool in_check_table; /* We are running check tables */
/* Marker if key_del_changed */
/* If info->keyread_buff can't be used for rnext */
my_bool page_changed;
diff --git a/storage/maria/maria_read_log.c b/storage/maria/maria_read_log.c
index de45eb0bcb6..7a630e274e3 100644
--- a/storage/maria/maria_read_log.c
+++ b/storage/maria/maria_read_log.c
@@ -192,14 +192,18 @@ static struct my_option my_long_options[] =
{"display-only", 'd', "display brief info read from records' header",
&opt_display_only, &opt_display_only, 0, GET_BOOL,
NO_ARG,0, 0, 0, 0, 0, 0},
+ { "end-lsn", 'e', "Stop applying at this lsn. If end-lsn is used, UNDO:s "
+ "will not be applied", &opt_end_lsn, &opt_end_lsn,
+ 0, GET_ULL, REQUIRED_ARG, 0, 0, ~(longlong) 0, 0, 0, 0 },
{"aria-log-dir-path", 'l',
"Path to the directory where to store transactional log",
(uchar **) &maria_data_root, (uchar **) &maria_data_root, 0,
GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
- { "page-buffer-size", 'P', "",
+ { "page-buffer-size", 'P',
+ "The size of the buffer used for index blocks for Maria tables",
&opt_page_buffer_size, &opt_page_buffer_size, 0,
GET_ULONG, REQUIRED_ARG, (long) USE_BUFFER_INIT,
- (long) USE_BUFFER_INIT, (long) ~(ulong) 0, (long) MALLOC_OVERHEAD,
+ 1024L*1024L, (long) ~(ulong) 0, (long) MALLOC_OVERHEAD,
(long) IO_SIZE, 0},
{ "start-from-lsn", 'o', "Start reading log from this lsn",
&opt_start_from_lsn, &opt_start_from_lsn,
@@ -207,15 +211,12 @@ static struct my_option my_long_options[] =
{"start-from-checkpoint", 'C', "Start applying from last checkpoint",
&opt_start_from_checkpoint, &opt_start_from_checkpoint, 0,
GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
- { "end-lsn", 'e', "Stop applying at this lsn. If end-lsn is used, UNDO:s "
- "will not be applied", &opt_end_lsn, &opt_end_lsn,
- 0, GET_ULL, REQUIRED_ARG, 0, 0, ~(longlong) 0, 0, 0, 0 },
{"silent", 's', "Print less information during apply/undo phase",
&opt_silent, &opt_silent, 0,
GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
- {"verbose", 'v', "Print more information during apply/undo phase",
- &maria_recovery_verbose, &maria_recovery_verbose, 0,
- GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
+ {"tables-to-redo", 'T',
+ "List of tables sepearated with , that we should apply REDO on. Use this if you only want to recover some tables",
+ 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
{"tmpdir", 't', "Path for temporary files. Multiple paths can be specified, "
"separated by "
#if defined( __WIN__) || defined(__NETWARE__)
@@ -227,6 +228,9 @@ static struct my_option my_long_options[] =
{"undo", 'u', "Apply UNDO records to tables. (disable with --disable-undo)",
(uchar **) &opt_apply_undo, (uchar **) &opt_apply_undo, 0,
GET_BOOL, NO_ARG, 1, 0, 0, 0, 0, 0},
+ {"verbose", 'v', "Print more information during apply/undo phase",
+ &maria_recovery_verbose, &maria_recovery_verbose, 0,
+ GET_BOOL, 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},
{ 0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
@@ -245,7 +249,7 @@ static void print_version(void)
static void usage(void)
{
print_version();
- puts("Copyright (C) 2007 MySQL AB");
+ puts("Copyright (C) 2007 MySQL AB, 2009-2011 Monty Program 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");
@@ -266,10 +270,18 @@ static void usage(void)
#include <help_end.h>
+static uchar* my_hash_get_string(const uchar *record, size_t *length,
+ my_bool first __attribute__ ((unused)))
+{
+ *length= (size_t) (strcend((const char*) record,',')- (const char*) record);
+ return (uchar*) record;
+}
+
+
static my_bool
get_one_option(int optid __attribute__((unused)),
const struct my_option *opt __attribute__((unused)),
- char *argument __attribute__((unused)))
+ char *argument)
{
switch (optid) {
case '?':
@@ -278,6 +290,23 @@ get_one_option(int optid __attribute__((unused)),
case 'V':
print_version();
exit(0);
+ case 'T':
+ {
+ char *pos;
+ if (!my_hash_inited(&tables_to_redo))
+ {
+ my_hash_init2(&tables_to_redo, 16, &my_charset_bin,
+ 16, 0, 0, my_hash_get_string, 0, HASH_UNIQUE);
+ }
+ do
+ {
+ pos= strcend(argument, ',');
+ if (pos != argument) /* Skip empty strings */
+ my_hash_insert(&tables_to_redo, (uchar*) argument);
+ argument= pos+1;
+ } while (*(pos++));
+ break;
+ }
#ifndef DBUG_OFF
case '#':
DBUG_SET_INITIAL(argument ? argument : default_dbug_option);
@@ -290,6 +319,7 @@ get_one_option(int optid __attribute__((unused)),
static void get_options(int *argc,char ***argv)
{
int ho_error;
+ my_bool need_help= 0;
if ((ho_error=handle_options(argc, argv, my_long_options, get_one_option)))
exit(ho_error);
@@ -297,8 +327,23 @@ static void get_options(int *argc,char ***argv)
if (!opt_apply)
opt_apply_undo= FALSE;
- if (((opt_display_only + opt_apply) != 1) || (*argc > 0))
+ if (*argc > 0)
+ {
+ need_help= 1;
+ fprintf(stderr, "Too many arguments given\n");
+ }
+ if ((opt_display_only + opt_apply) != 1)
+ {
+ need_help= 1;
+ fprintf(stderr,
+ "You must use one and only one of the options 'display-only' or "
+ "'apply'\n");
+ }
+
+ if (need_help)
{
+ fflush(stderr);
+ need_help =1;
usage();
exit(1);
}
diff --git a/storage/myisam/ha_myisam.cc b/storage/myisam/ha_myisam.cc
index b32cce9e43c..5755c275d2e 100644
--- a/storage/myisam/ha_myisam.cc
+++ b/storage/myisam/ha_myisam.cc
@@ -552,6 +552,7 @@ ha_myisam::ha_myisam(handlerton *hton, TABLE_SHARE *table_arg)
:handler(hton, table_arg), file(0),
int_table_flags(HA_NULL_IN_KEY | HA_CAN_FULLTEXT | HA_CAN_SQL_HANDLER |
HA_BINLOG_ROW_CAPABLE | HA_BINLOG_STMT_CAPABLE |
+ HA_CAN_VIRTUAL_COLUMNS |
HA_DUPLICATE_POS | HA_CAN_INDEX_BLOBS | HA_AUTO_PART_KEY |
HA_FILE_BASED | HA_CAN_GEOMETRY | HA_NO_TRANSACTIONS |
HA_CAN_INSERT_DELAYED | HA_CAN_BIT_FIELD | HA_CAN_RTREEKEYS |
diff --git a/storage/myisam/ha_myisam.h b/storage/myisam/ha_myisam.h
index 210dc7bc745..b9700e0ef90 100644
--- a/storage/myisam/ha_myisam.h
+++ b/storage/myisam/ha_myisam.h
@@ -141,7 +141,6 @@ class ha_myisam: public handler
int assign_to_keycache(THD* thd, HA_CHECK_OPT* check_opt);
int preload_keys(THD* thd, HA_CHECK_OPT* check_opt);
bool check_if_incompatible_data(HA_CREATE_INFO *info, uint table_changes);
- bool check_if_supported_virtual_columns(void) { return TRUE;}
#ifdef HAVE_REPLICATION
int dump(THD* thd, int fd);
int net_read_dump(NET* net);
diff --git a/storage/myisammrg/ha_myisammrg.cc b/storage/myisammrg/ha_myisammrg.cc
index c61d65572cb..ff2d01e09c0 100644
--- a/storage/myisammrg/ha_myisammrg.cc
+++ b/storage/myisammrg/ha_myisammrg.cc
@@ -1057,9 +1057,9 @@ THR_LOCK_DATA **ha_myisammrg::store_lock(THD *thd,
/*
When MERGE table is open, but not yet attached, other threads
- could flush it, which means call mysql_lock_abort_for_thread()
+ could flush it, which means calling mysql_lock_abort_for_thread()
on this threads TABLE. 'children_attached' is FALSE in this
- situaton. Since the table is not locked, return no lock data.
+ situation. Since the table is not locked, return no lock data.
*/
if (!this->file->children_attached)
goto end; /* purecov: tested */
diff --git a/storage/ndb/include/ndbapi/NdbError.hpp b/storage/ndb/include/ndbapi/NdbError.hpp
index aa27caf78f9..b752e578bc1 100644
--- a/storage/ndb/include/ndbapi/NdbError.hpp
+++ b/storage/ndb/include/ndbapi/NdbError.hpp
@@ -66,7 +66,7 @@ struct NdbError {
/**
* The error code indicates a permanent error.<br>
- * (Includes classificatons: NdbError::PermanentError,
+ * (Includes classifications: NdbError::PermanentError,
* NdbError::ApplicationError, NdbError::NoDataFound,
* NdbError::ConstraintViolation, NdbError::SchemaError,
* NdbError::UserDefinedError, NdbError::InternalError, and,
diff --git a/storage/ndb/include/util/File.hpp b/storage/ndb/include/util/File.hpp
index b9d348683ec..bbddc24583a 100644
--- a/storage/ndb/include/util/File.hpp
+++ b/storage/ndb/include/util/File.hpp
@@ -31,7 +31,7 @@ public:
* Returns time for last contents modification of a file.
*
* @param aFileName a filename to check.
- * @return the time for last contents modificaton of the file.
+ * @return the time for last contents modification of the file.
*/
static time_t mtime(const char* aFileName);
diff --git a/storage/xtradb/handler/ha_innodb.cc b/storage/xtradb/handler/ha_innodb.cc
index 39621852c4c..f0a62d10617 100644
--- a/storage/xtradb/handler/ha_innodb.cc
+++ b/storage/xtradb/handler/ha_innodb.cc
@@ -1480,7 +1480,7 @@ UNIV_INTERN
ha_innobase::ha_innobase(handlerton *hton, TABLE_SHARE *table_arg)
:handler(hton, table_arg),
int_table_flags(HA_REC_NOT_IN_SEQ |
- HA_NULL_IN_KEY |
+ HA_NULL_IN_KEY | HA_CAN_VIRTUAL_COLUMNS |
HA_CAN_INDEX_BLOBS |
HA_CAN_SQL_HANDLER |
HA_PRIMARY_KEY_REQUIRED_FOR_POSITION |
diff --git a/storage/xtradb/trx/trx0sys.c b/storage/xtradb/trx/trx0sys.c
index 566f7c95793..a9fb5bb38d8 100644
--- a/storage/xtradb/trx/trx0sys.c
+++ b/storage/xtradb/trx/trx0sys.c
@@ -1667,7 +1667,7 @@ trx_sys_print_mysql_binlog_offset_from_page(
/* THESE ARE COPIED FROM NON-HOTBACKUP PART OF THE INNODB SOURCE TREE
- (This code duplicaton should be fixed at some point!)
+ (This code duplication should be fixed at some point!)
*/
#define TRX_SYS_SPACE 0 /* the SYSTEM tablespace */