Merge lp:~percona-dev/percona-server/release-5.5.10-20.1 into lp:~percona-dev/percona-server/5.5.10

Proposed by Ignacio Nin
Status: Merged
Approved by: Alexey Kopytov
Approved revision: no longer in the source branch.
Merged at revision: 95
Proposed branch: lp:~percona-dev/percona-server/release-5.5.10-20.1
Merge into: lp:~percona-dev/percona-server/5.5.10
Diff against target: 41116 lines
To merge this branch: bzr merge lp:~percona-dev/percona-server/release-5.5.10-20.1
Reviewer Review Type Date Requested Status
Alexey Kopytov (community) Approve
Review via email: mp+57268@code.launchpad.net

Description of the change

Addition of scripts and fixes for building in all platforms. Includes HandlerSocket and the Maatkit UDF functions.

To post a comment you must log in.
Revision history for this message
Alexey Kopytov (akopytov) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added directory 'HandlerSocket-Plugin-for-MySQL'
2=== added file 'HandlerSocket-Plugin-for-MySQL/AUTHORS'
3--- HandlerSocket-Plugin-for-MySQL/AUTHORS 1970-01-01 00:00:00 +0000
4+++ HandlerSocket-Plugin-for-MySQL/AUTHORS 2011-04-12 04:16:24 +0000
5@@ -0,0 +1,19 @@
6+Akira Higuchi (https://github.com/ahiguti)
7+ - developed HanderSocket plugin, libhsclient, and perl-Net-HandlerSocket
8+
9+Yoshinori Matsunobu (https://github.com/yoshinorim)
10+ - introduced autotools, added support for MySQL 5.5.6, added statistics
11+ variables
12+
13+Jeff Hodges (https://github.com/jmhodges)
14+ - fixed some autotools scripts
15+
16+Toru Yamaguchi (https://github.com/zigorou)
17+ - ported to MacOS X
18+
19+Moriyoshi Koizumi (https://github.com/moriyoshi)
20+ - fixed some autotools scripts
21+
22+takeda-at (https://github.com/takada-at)
23+ - added simple authorization function
24+
25
26=== added file 'HandlerSocket-Plugin-for-MySQL/COPYING'
27--- HandlerSocket-Plugin-for-MySQL/COPYING 1970-01-01 00:00:00 +0000
28+++ HandlerSocket-Plugin-for-MySQL/COPYING 2011-04-12 04:16:24 +0000
29@@ -0,0 +1,30 @@
30+-----------------------------------------------------------------------------
31+HandlerSocket plugin for MySQL
32+
33+ Copyright (c) 2010 DeNA Co.,Ltd.
34+ All rights reserved.
35+
36+ Redistribution and use in source and binary forms, with or without
37+ modification, are permitted provided that the following conditions are met:
38+
39+ * Redistributions of source code must retain the above copyright
40+ notice, this list of conditions and the following disclaimer.
41+ * Redistributions in binary form must reproduce the above copyright
42+ notice, this list of conditions and the following disclaimer in the
43+ documentation and/or other materials provided with the distribution.
44+ * Neither the name of DeNA Co.,Ltd. nor the names of its contributors
45+ may be used to endorse or promote products derived from this software
46+ without specific prior written permission.
47+
48+ THIS SOFTWARE IS PROVIDED BY DeNA Co.,Ltd. "AS IS" AND ANY EXPRESS OR
49+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
50+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
51+ EVENT SHALL DeNA Co.,Ltd. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
52+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
53+ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
54+ OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
55+ WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
56+ OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
57+ ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
58+
59+
60
61=== added file 'HandlerSocket-Plugin-for-MySQL/ChangeLog'
62--- HandlerSocket-Plugin-for-MySQL/ChangeLog 1970-01-01 00:00:00 +0000
63+++ HandlerSocket-Plugin-for-MySQL/ChangeLog 2011-04-12 04:16:24 +0000
64@@ -0,0 +1,12 @@
65+1.0.6 - 2010-10-29
66+ * Changed build instruction (autoreconf/configure/make), removed auto-generated files (Contributed by jmhodges)
67+ *
68+
69+1.0.5 - 2010-10-18
70+ * Changed build procedures (using typical configure/make)
71+ * Supported 5.5.6
72+ * Added status variables
73+
74+1.0.4 - 2010-08-15
75+ * Initial public release
76+
77
78=== added file 'HandlerSocket-Plugin-for-MySQL/Makefile.am'
79--- HandlerSocket-Plugin-for-MySQL/Makefile.am 1970-01-01 00:00:00 +0000
80+++ HandlerSocket-Plugin-for-MySQL/Makefile.am 2011-04-12 04:16:24 +0000
81@@ -0,0 +1,87 @@
82+
83+ACLOCAL_AMFLAGS = -I m4
84+
85+SUBDIRS = @HANDLERSOCKET_SUBDIRS@
86+
87+perl:
88+ cd perl-Net-HandlerSocket && perl Makefile.PL && make
89+
90+install_perl:
91+ cd perl-Net-HandlerSocket && make install
92+
93+rpms: rpm_cli rpm_perl rpm_c
94+
95+rpm_dir:
96+ - mkdir dist
97+ - mkdir dist/BUILD dist/RPMS dist/SOURCES dist/SPECS dist/SRPMS
98+
99+rpm_cli: clean_cli rpm_dir
100+ sed -e "s/HANDLERSOCKET_VERSION/$(VERSION)/" \
101+ libhsclient/libhsclient.spec.template \
102+ > libhsclient/libhsclient.spec
103+ tar cvfz dist/libhsclient.tar.gz libhsclient
104+ rpmbuild --define "_topdir `pwd`/dist" -ta \
105+ dist/libhsclient.tar.gz
106+
107+rpm_perl: clean_perl rpm_dir
108+ sed -e "s/HANDLERSOCKET_VERSION/$(VERSION)/" \
109+ perl-Net-HandlerSocket/perl-Net-HandlerSocket.spec.template \
110+ > perl-Net-HandlerSocket/perl-Net-HandlerSocket.spec
111+ cd perl-Net-HandlerSocket && perl Makefile.PL && make clean && \
112+ rm -f Makefile.old
113+ tar cvfz dist/perl-Net-HandlerSocket.tar.gz perl-Net-HandlerSocket
114+ rpmbuild --define "_topdir `pwd`/dist" -ta \
115+ dist/perl-Net-HandlerSocket.tar.gz
116+
117+rpm_c: clean_c rpm_dir
118+ sed -e "s/HANDLERSOCKET_VERSION/$(VERSION)/" \
119+ handlersocket/handlersocket.spec.template \
120+ > handlersocket/handlersocket.spec
121+ sed -e "s|HANDLERSOCKET_MYSQL_INC|$(MYSQL_CFLAGS) $(MYSQL_INC)|" \
122+ -e "s|HANDLERSOCKET_MYSQL_LIB|$(MYSQL_LIB)|" \
123+ handlersocket/Makefile.plain.template \
124+ > handlersocket/Makefile.plain
125+ tar cvfz dist/handlersocket.tar.gz handlersocket
126+ rpmbuild --define "_topdir `pwd`/dist" -ta \
127+ dist/handlersocket.tar.gz
128+
129+install_rpm_pl:
130+ - sudo rpm -e perl-Net-HandlerSocket
131+ - sudo rpm -e perl-Net-HandlerSocket-debuginfo
132+ make clean
133+ make rpm_perl
134+ - sudo rpm -U dist/RPMS/*/perl*.rpm
135+
136+installrpms:
137+ - sudo rpm -e handlersocket
138+ - sudo rpm -e handlersocket-debuginfo
139+ - sudo rpm -e perl-Net-HandlerSocket
140+ - sudo rpm -e perl-Net-HandlerSocket-debuginfo
141+ - sudo rpm -e libhsclient
142+ - sudo rpm -e libhsclient-debuginfo
143+ make clean
144+ make rpm_cli
145+ - sudo rpm -U dist/RPMS/*/libhsclient*.rpm
146+ make clean
147+ make rpm_perl
148+ - sudo rpm -U dist/RPMS/*/perl*.rpm
149+ make clean
150+ make rpm_c
151+ - sudo rpm -U dist/RPMS/*/handlersocket*.rpm
152+
153+clean_cli:
154+ cd libhsclient && make clean
155+ cd client && make clean
156+
157+clean_perl:
158+ cd perl-Net-HandlerSocket && perl Makefile.PL && make clean && \
159+ rm -f Makefile.old
160+
161+clean_c:
162+ cd handlersocket && make clean
163+
164+clean_all: clean_cli clean_perl clean_c
165+ cd regtest && make clean
166+ rm -rf dist/*/*
167+ rm -f dist/*.tar.gz
168+
169
170=== added file 'HandlerSocket-Plugin-for-MySQL/README'
171--- HandlerSocket-Plugin-for-MySQL/README 1970-01-01 00:00:00 +0000
172+++ HandlerSocket-Plugin-for-MySQL/README 2011-04-12 04:16:24 +0000
173@@ -0,0 +1,76 @@
174+
175+-----------------------------------------------------------------------------
176+HandlerSocket plugin for MySQL
177+
178+Copyright (c) 2010 DeNA Co.,Ltd.
179+All rights reserved.
180+
181+Redistribution and use in source and binary forms, with or without
182+modification, are permitted provided that the following conditions are met:
183+
184+ * Redistributions of source code must retain the above copyright
185+ notice, this list of conditions and the following disclaimer.
186+ * Redistributions in binary form must reproduce the above copyright
187+ notice, this list of conditions and the following disclaimer in the
188+ documentation and/or other materials provided with the distribution.
189+ * Neither the name of DeNA Co.,Ltd. nor the names of its contributors
190+ may be used to endorse or promote products derived from this software
191+ without specific prior written permission.
192+
193+THIS SOFTWARE IS PROVIDED BY DeNA Co.,Ltd. "AS IS" AND ANY EXPRESS OR
194+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
195+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
196+EVENT SHALL DeNA Co.,Ltd. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
197+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
198+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
199+OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
200+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
201+OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
202+ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
203+
204+
205+-----------------------------------------------------------------------------
206+About HandlerSocket
207+
208+HandlerSocket is a NoSQL plugin for MySQL. It works as a daemon inside the
209+mysqld process, accept tcp connections, and execute requests from clients.
210+HandlerSocket does not support SQL queries. Instead, it supports simple CRUD
211+operations on tables.
212+
213+Because of the following reasons, HandlerSocket is much faster than the
214+mysqld/libmysql pair in some circumstances:
215+
216+ - HandlerSocket manipulates data without parsing SQL, which causes less
217+ CPU usage.
218+ - HandlerSocket reads many requests from clients and executes their
219+ requests in bulk, which causes less CPU and disk usage.
220+ - HandlerSocket client/server protocol is more compact than the
221+ mysql/libmysql pair, which causes less network usage.
222+
223+The current version of HandlerSocket only works with GNU/Linux. The source
224+archive of HandlerSocket includes a C++ and a Perl client libraries.
225+Here is a list of client libraries for other languages:
226+
227+ - PHP
228+ http://openpear.org/package/Net_HandlerSocket
229+ http://github.com/tz-lom/HSPHP
230+ http://code.google.com/p/php-handlersocket/
231+ - Java
232+ http://code.google.com/p/hs4j/
233+ http://code.google.com/p/handlersocketforjava/
234+ - Python
235+ http://pypi.python.org/pypi/python-handler-socket
236+ https://code.launchpad.net/~songofacandy/+junk/pyhandlersocket
237+ - Ruby
238+ https://github.com/winebarrel/ruby-handlersocket
239+ https://github.com/miyucy/handlersocket
240+ - JavaScript
241+ https://github.com/koichik/node-handlersocket
242+ - Scala
243+ https://github.com/fujohnwang/hs2client
244+
245+The home of HandlerSocket is here:
246+ https://github.com/ahiguti/HandlerSocket-Plugin-for-MySQL
247+
248+More documents are available in docs-en/ and docs-ja/ directories.
249+
250
251=== added file 'HandlerSocket-Plugin-for-MySQL/autogen.sh'
252--- HandlerSocket-Plugin-for-MySQL/autogen.sh 1970-01-01 00:00:00 +0000
253+++ HandlerSocket-Plugin-for-MySQL/autogen.sh 2011-04-12 04:16:24 +0000
254@@ -0,0 +1,117 @@
255+#!/bin/sh
256+
257+warn() {
258+echo -e "\tWARNING: $@" 1>&2
259+}
260+
261+# init
262+
263+LIBTOOLIZE=libtoolize
264+ACLOCAL=aclocal
265+AUTOCONF=autoconf
266+AUTOHEADER=autoheader
267+AUTOMAKE=automake
268+
269+case `uname -s` in
270+Darwin)
271+LIBTOOLIZE=glibtoolize
272+;;
273+FreeBSD)
274+ACLOCAL_ARGS="$ACLOCAL_ARGS -I /usr/local/share/aclocal/"
275+;;
276+esac
277+
278+
279+# libtoolize
280+echo "Searching libtoolize..."
281+if [ `which $LIBTOOLIZE` ] ; then
282+echo -e "\tFOUND: libtoolize -> $LIBTOOLIZE"
283+else
284+warn "Cannot Found libtoolize... input libtool command"
285+ read LIBTOOLIZE
286+ LIBTOOLIZE=`which $LIBTOOLIZE`
287+ if [ `which $LIBTOOLIZE` ] ; then
288+echo -e "\tSET: libtoolize -> $LIBTOOLIZE"
289+ else
290+warn "$LIBTOOLIZE: Command not found."
291+ exit 1;
292+ fi
293+fi
294+
295+# aclocal
296+echo "Searching aclocal..."
297+if [ `which $ACLOCAL` ] ; then
298+echo -e "\tFOUND: aclocal -> $ACLOCAL"
299+else
300+warn "Cannot Found aclocal... input aclocal command"
301+ read ACLOCAL
302+ ACLOCAL=`which $ACLOCAL`
303+ if [ `which $ACLOCAL` ] ; then
304+echo -e "\tSET: aclocal -> $ACLOCAL"
305+ else
306+warn "$ACLOCAL: Command not found."
307+ exit 1;
308+ fi
309+fi
310+
311+# automake
312+echo "Searching automake..."
313+if [ `which $AUTOMAKE` ] ; then
314+echo -e "\tFOUND: automake -> $AUTOMAKE"
315+else
316+warn "Cannot Found automake... input automake command"
317+ read AUTOMAKE
318+ ACLOCAL=`which $AUTOMAKE`
319+ if [ `which $AUTOMAKE` ] ; then
320+echo -e "\tSET: automake -> $AUTOMAKE"
321+ else
322+warn "$AUTOMAKE: Command not found."
323+ exit 1;
324+ fi
325+fi
326+
327+# autoheader
328+echo "Searching autoheader..."
329+if [ `which $AUTOHEADER` ] ; then
330+echo -e "\tFOUND: autoheader -> $AUTOHEADER"
331+else
332+warn "Cannot Found autoheader... input autoheader command"
333+ read AUTOHEADER
334+ ACLOCAL=`which $AUTOHEADER`
335+ if [ `which $AUTOHEADER` ] ; then
336+echo -e "\tSET: autoheader -> $AUTOHEADER"
337+ else
338+warn "$AUTOHEADER: Command not found."
339+ exit 1;
340+ fi
341+fi
342+
343+# autoconf
344+echo "Searching autoconf..."
345+if [ `which $AUTOCONF` ] ; then
346+echo -e "\tFOUND: autoconf -> $AUTOCONF"
347+else
348+warn "Cannot Found autoconf... input autoconf command"
349+ read AUTOCONF
350+ ACLOCAL=`which $AUTOCONF`
351+ if [ `which $AUTOCONF` ] ; then
352+echo -e "\tSET: autoconf -> $AUTOCONF"
353+ else
354+warn "$AUTOCONF: Command not found."
355+ exit 1;
356+ fi
357+fi
358+
359+echo "Running libtoolize ..."
360+$LIBTOOLIZE --force --copy
361+echo "Running aclocal ..."
362+$ACLOCAL ${ACLOCAL_ARGS} -I .
363+echo "Running autoheader..."
364+$AUTOHEADER
365+echo "Running automake ..."
366+$AUTOMAKE --add-missing --copy
367+echo "Running autoconf ..."
368+$AUTOCONF
369+
370+#mkdir m4 2> /dev/null
371+
372
373=== added directory 'HandlerSocket-Plugin-for-MySQL/client'
374=== added file 'HandlerSocket-Plugin-for-MySQL/client/Makefile.am'
375--- HandlerSocket-Plugin-for-MySQL/client/Makefile.am 1970-01-01 00:00:00 +0000
376+++ HandlerSocket-Plugin-for-MySQL/client/Makefile.am 2011-04-12 04:16:24 +0000
377@@ -0,0 +1,13 @@
378+AM_INCLUDES= -I../libhsclient
379+bin_PROGRAMS=hsclient
380+hsclient_SOURCES= hsclient.cpp
381+hsclient_LDFLAGS= -static -L../libhsclient -lhsclient
382+hsclient_CXXFLAGS= $(AM_INCLUDES)
383+
384+hstest: hstest.o
385+ $(CXX) $(CXXFLAGS) $(LFLAGS) hstest.o \
386+ -L../libhsclient/.libs -lhsclient $(MYSQL_LIB) -o hstest
387+
388+hstest.o: hstest.cpp
389+ $(CXX) $(CXXFLAGS) $(MYSQL_INC) $(AM_INCLUDES) -c hstest.cpp
390+
391
392=== added file 'HandlerSocket-Plugin-for-MySQL/client/hsclient.cpp'
393--- HandlerSocket-Plugin-for-MySQL/client/hsclient.cpp 1970-01-01 00:00:00 +0000
394+++ HandlerSocket-Plugin-for-MySQL/client/hsclient.cpp 2011-04-12 04:16:24 +0000
395@@ -0,0 +1,88 @@
396+
397+// vim:sw=2:ai
398+
399+#include "hstcpcli.hpp"
400+#include "string_util.hpp"
401+
402+namespace dena {
403+
404+int
405+hstcpcli_main(int argc, char **argv)
406+{
407+ config conf;
408+ parse_args(argc, argv, conf);
409+ socket_args sockargs;
410+ sockargs.set(conf);
411+ hstcpcli_ptr cli = hstcpcli_i::create(sockargs);
412+ const std::string dbname = conf.get_str("dbname", "hstest");
413+ const std::string table = conf.get_str("table", "hstest_table1");
414+ const std::string index = conf.get_str("index", "PRIMARY");
415+ const std::string fields = conf.get_str("fields", "k,v");
416+ const int limit = conf.get_int("limit", 0);
417+ const int skip = conf.get_int("skip", 0);
418+ std::vector<std::string> keys;
419+ std::vector<string_ref> keyrefs;
420+ size_t num_keys = 0;
421+ while (true) {
422+ const std::string conf_key = std::string("k") + to_stdstring(num_keys);
423+ const std::string k = conf.get_str(conf_key, "");
424+ const std::string kx = conf.get_str(conf_key, "x");
425+ if (k.empty() && kx == "x") {
426+ break;
427+ }
428+ ++num_keys;
429+ keys.push_back(k);
430+ }
431+ for (size_t i = 0; i < keys.size(); ++i) {
432+ const string_ref ref(keys[i].data(), keys[i].size());
433+ keyrefs.push_back(ref);
434+ }
435+ const std::string op = conf.get_str("op", "=");
436+ const string_ref op_ref(op.data(), op.size());
437+ cli->request_buf_open_index(0, dbname.c_str(), table.c_str(),
438+ index.c_str(), fields.c_str());
439+ cli->request_buf_exec_generic(0, op_ref, num_keys == 0 ? 0 : &keyrefs[0],
440+ num_keys, limit, skip, string_ref(), 0, 0);
441+ int code = 0;
442+ size_t numflds = 0;
443+ do {
444+ if (cli->request_send() != 0) {
445+ fprintf(stderr, "request_send: %s\n", cli->get_error().c_str());
446+ break;
447+ }
448+ if ((code = cli->response_recv(numflds)) != 0) {
449+ fprintf(stderr, "response_recv: %s\n", cli->get_error().c_str());
450+ break;
451+ }
452+ } while (false);
453+ cli->response_buf_remove();
454+ do {
455+ if ((code = cli->response_recv(numflds)) != 0) {
456+ fprintf(stderr, "response_recv: %s\n", cli->get_error().c_str());
457+ break;
458+ }
459+ while (true) {
460+ const string_ref *const row = cli->get_next_row();
461+ if (row == 0) {
462+ break;
463+ }
464+ printf("REC:");
465+ for (size_t i = 0; i < numflds; ++i) {
466+ const std::string val(row[i].begin(), row[i].size());
467+ printf(" %s", val.c_str());
468+ }
469+ printf("\n");
470+ }
471+ } while (false);
472+ cli->response_buf_remove();
473+ return 0;
474+}
475+
476+};
477+
478+int
479+main(int argc, char **argv)
480+{
481+ return dena::hstcpcli_main(argc, argv);
482+}
483+
484
485=== added file 'HandlerSocket-Plugin-for-MySQL/client/hspool_test.pl'
486--- HandlerSocket-Plugin-for-MySQL/client/hspool_test.pl 1970-01-01 00:00:00 +0000
487+++ HandlerSocket-Plugin-for-MySQL/client/hspool_test.pl 2011-04-12 04:16:24 +0000
488@@ -0,0 +1,224 @@
489+#!/usr/bin/perl
490+
491+use strict;
492+use warnings;
493+use DB::HandlerSocket::Pool;
494+use DBI;
495+
496+my %conf = ();
497+for my $i (@ARGV) {
498+ my ($k, $v) = split(/=/, $i);
499+ $conf{$k} = $v;
500+}
501+
502+my $verbose = get_conf("verbose", 0);
503+my $actions_str = get_conf("actions",
504+ "create,insert,verify,verify2,verify3,verify4,clean");
505+my $tablesize = get_conf("tablesize", 1000);
506+my $db = get_conf("db", "hstestdb");
507+my $table = get_conf("table", "testtbl");
508+my $table_schema = get_conf("table_schema", undef);
509+my $engine = get_conf("engine", "innodb");
510+my $host = get_conf("host", "localhost");
511+my $mysqlport = get_conf("mysqlport", 3306);
512+my $hsport_rd = get_conf("hsport_rd", 9998);
513+my $hsport_wr = get_conf("hsport_wr", 9999);
514+my $loop = get_conf("loop", 10000);
515+my $op = get_conf("op", "=");
516+my $ssps = get_conf("ssps", 0);
517+my $num_moreflds = get_conf("moreflds", 0);
518+my $moreflds_prefix = get_conf("moreflds_prefix", "f");
519+my $mysql_user = 'root';
520+my $mysql_password = '';
521+
522+my $dsn = "DBI:mysql:database=;host=$host;port=$mysqlport"
523+ . ";mysql_server_prepare=$ssps";
524+my $dbh = DBI->connect($dsn, $mysql_user, $mysql_password,
525+ { RaiseError => 1 });
526+my $hsargs = { 'host' => $host, 'port' => $hsport_rd };
527+my $hspool = new DB::HandlerSocket::Pool({
528+ hostmap => {
529+ "$db.$table" => {
530+ host => $host,
531+ port => $hsport_rd,
532+ },
533+ },
534+ resolve => undef,
535+ error => undef,
536+});
537+$table_schema = "(k int primary key, fc30 varchar(30), ft text)"
538+ if (!defined($table_schema));
539+
540+my @actions = split(/,/, $actions_str);
541+for my $action (@actions) {
542+ print "ACTION: $action\n";
543+ eval "hstest_$action()";
544+ if ($@) {
545+ die $@;
546+ }
547+ print "ACTION: $action DONE\n";
548+}
549+
550+sub get_conf {
551+ my ($key, $def) = @_;
552+ my $val = $conf{$key};
553+ if ($val) {
554+ print "$key=$val\n";
555+ } else {
556+ $val = $def;
557+ my $defstr = $def || "(undef)";
558+ print "$key=$defstr(default)\n";
559+ }
560+ return $val;
561+}
562+
563+sub hstest_create {
564+ $dbh->do("drop database if exists $db");
565+ $dbh->do("create database $db");
566+ $dbh->do("use $db");
567+ $dbh->do("create table $table $table_schema engine=$engine");
568+}
569+
570+sub hstest_dump {
571+ $dbh->do("use $db");
572+ my $sth = $dbh->prepare("select * from $table");
573+ $sth->execute();
574+ my $arr = $sth->fetchall_arrayref();
575+ for my $rec (@$arr) {
576+ print "REC:";
577+ for my $row (@$rec) {
578+ print " $row";
579+ }
580+ print "\n";
581+ }
582+}
583+
584+sub hstest_insert {
585+ $dbh->do("use $db");
586+ my $sth = $dbh->prepare("insert into $table values (?, ?, ?)");
587+ for (my $k = 0; $k < $tablesize; ++$k) {
588+ my $fc30 = "fc30_$k";
589+ my $ft = "ft_$k";
590+ $sth->execute($k, $fc30, $ft);
591+ }
592+}
593+
594+sub hstest_verify {
595+ $dbh->do("use $db");
596+ my $sth = $dbh->prepare("select * from $table order by k");
597+ $sth->execute();
598+ my $arr = $sth->fetchall_arrayref();
599+ my $hsres = $hspool->index_find($db, $table, "PRIMARY", "k,fc30,ft",
600+ ">=", [ 0 ], $tablesize, 0);
601+ for (my $i = 0; $i < $tablesize; ++$i) {
602+ my $rec = $arr->[$i];
603+ my $differ = 0;
604+ print "REC:" if $verbose;
605+ for (my $j = 0; $j < 3; ++$j) {
606+ my $fld = $rec->[$j];
607+ my $hsidx = $i * 3 + $j;
608+ my $hsfld = $hsres->[$hsidx];
609+ if ($hsfld ne $fld) {
610+ $differ = 1;
611+ }
612+ if ($differ) {
613+ print " $fld:$hsfld" if $verbose;
614+ } else {
615+ print " $hsfld" if $verbose;
616+ }
617+ }
618+ print "\n" if $verbose;
619+ if ($differ) {
620+ die "verification failed";
621+ }
622+ }
623+}
624+
625+sub hstest_verify2 {
626+ $dbh->do("use $db");
627+ my $sth = $dbh->prepare("select * from $table order by k");
628+ $sth->execute();
629+ my $arr = $sth->fetchall_arrayref();
630+ my $hsresa = $hspool->index_find_multi($db, $table, "PRIMARY",
631+ "k,fc30,ft", [ [ -1, ">=", [ 0 ], $tablesize, 0 ] ]);
632+ my $hsres = $hsresa->[0];
633+ for (my $i = 0; $i < $tablesize; ++$i) {
634+ my $rec = $arr->[$i];
635+ my $differ = 0;
636+ print "REC:" if $verbose;
637+ for (my $j = 0; $j < 3; ++$j) {
638+ my $fld = $rec->[$j];
639+ my $hsidx = $i * 3 + $j;
640+ my $hsfld = $hsres->[$hsidx];
641+ if ($hsfld ne $fld) {
642+ $differ = 1;
643+ }
644+ if ($differ) {
645+ print " $fld:$hsfld" if $verbose;
646+ } else {
647+ print " $hsfld" if $verbose;
648+ }
649+ }
650+ print "\n" if $verbose;
651+ if ($differ) {
652+ die "verification failed";
653+ }
654+ }
655+}
656+
657+sub hashref_to_str {
658+ my $href = $_[0];
659+ my $r = '';
660+ for my $k (sort keys %$href) {
661+ my $v = $href->{$k};
662+ $r .= "($k=>$v)";
663+ }
664+ return $r;
665+}
666+
667+sub hstest_verify3 {
668+ $dbh->do("use $db");
669+ my $sth = $dbh->prepare("select * from $table order by k");
670+ $sth->execute();
671+ my $hsres_t = $hspool->index_find($db, $table, "PRIMARY", "k,fc30,ft",
672+ ">=", [ 0 ], $tablesize, 0);
673+ my $hsres = DB::HandlerSocket::Pool::result_single_to_hasharr(
674+ [ 'k', 'fc30', 'ft' ], $hsres_t);
675+ for (my $i = 0; $i < $tablesize; ++$i) {
676+ my $mystr = hashref_to_str($sth->fetchrow_hashref());
677+ my $hsstr = hashref_to_str($hsres->[$i]);
678+ if ($mystr ne $hsstr) {
679+ print "DIFF my=[$mystr] hs=[$hsstr]\n" if $verbose;
680+ die "verification failed";
681+ } else {
682+ print "OK $hsstr\n" if $verbose;
683+ }
684+ }
685+}
686+
687+sub hstest_verify4 {
688+ $dbh->do("use $db");
689+ my $sth = $dbh->prepare("select * from $table order by k");
690+ $sth->execute();
691+ my $hsres_t = $hspool->index_find($db, $table, "PRIMARY", "k,fc30,ft",
692+ ">=", [ 0 ], $tablesize, 0);
693+ my $hsres = DB::HandlerSocket::Pool::result_single_to_hashhash(
694+ [ 'k', 'fc30', 'ft' ], 'k', $hsres_t);
695+ my $rechash = $sth->fetchall_hashref('k');
696+ while (my ($k, $href) = each (%$rechash)) {
697+ my $mystr = hashref_to_str($href);
698+ my $hsstr = hashref_to_str($hsres->{$k});
699+ if ($mystr ne $hsstr) {
700+ print "DIFF my=[$mystr] hs=[$hsstr]\n" if $verbose;
701+ die "verification failed";
702+ } else {
703+ print "OK $hsstr\n" if $verbose;
704+ }
705+ }
706+}
707+
708+sub hstest_clean {
709+ $hspool->clear_pool();
710+ $dbh->do("drop database if exists $db");
711+}
712+
713
714=== added file 'HandlerSocket-Plugin-for-MySQL/client/hstest.cpp'
715--- HandlerSocket-Plugin-for-MySQL/client/hstest.cpp 1970-01-01 00:00:00 +0000
716+++ HandlerSocket-Plugin-for-MySQL/client/hstest.cpp 2011-04-12 04:16:24 +0000
717@@ -0,0 +1,1494 @@
718+
719+// vim:sw=2:ai
720+
721+#include <signal.h>
722+#include <sys/time.h>
723+#include <stdio.h>
724+#include <string.h>
725+#include <vector>
726+#include <stdlib.h>
727+#include <memory>
728+#include <errno.h>
729+#include <mysql.h>
730+#include <time.h>
731+#include <sys/types.h>
732+#include <sys/stat.h>
733+#include <fcntl.h>
734+
735+#include "util.hpp"
736+#include "auto_ptrcontainer.hpp"
737+#include "socket.hpp"
738+#include "thread.hpp"
739+#include "hstcpcli.hpp"
740+
741+#if __GNUC__ >= 4
742+long atomic_exchange_and_add(volatile long *valp, long c)
743+{
744+ return __sync_fetch_and_add(valp, c);
745+}
746+#else
747+#include <bits/atomicity.h>
748+using namespace __gnu_cxx;
749+long atomic_exchange_and_add(volatile long *valp, long c)
750+{
751+ return __exchange_and_add((volatile _Atomic_word *)valp, c);
752+}
753+#endif
754+
755+namespace dena {
756+
757+struct auto_mysql : private noncopyable {
758+ auto_mysql() : db(0) {
759+ reset();
760+ }
761+ ~auto_mysql() {
762+ if (db) {
763+ mysql_close(db);
764+ }
765+ }
766+ void reset() {
767+ if (db) {
768+ mysql_close(db);
769+ }
770+ if ((db = mysql_init(0)) == 0) {
771+ fatal_abort("failed to initialize mysql client");
772+ }
773+ }
774+ operator MYSQL *() const { return db; }
775+ private:
776+ MYSQL *db;
777+};
778+
779+struct auto_mysql_res : private noncopyable {
780+ auto_mysql_res(MYSQL *db) {
781+ res = mysql_store_result(db);
782+ }
783+ ~auto_mysql_res() {
784+ if (res) {
785+ mysql_free_result(res);
786+ }
787+ }
788+ operator MYSQL_RES *() const { return res; }
789+ private:
790+ MYSQL_RES *res;
791+};
792+
793+struct auto_mysql_stmt : private noncopyable {
794+ auto_mysql_stmt(MYSQL *db) {
795+ stmt = mysql_stmt_init(db);
796+ }
797+ ~auto_mysql_stmt() {
798+ if (stmt) {
799+ mysql_stmt_close(stmt);
800+ }
801+ }
802+ operator MYSQL_STMT *() const { return stmt; }
803+ private:
804+ MYSQL_STMT *stmt;
805+};
806+
807+namespace {
808+
809+double
810+gettimeofday_double()
811+{
812+ struct timeval tv = { };
813+ if (gettimeofday(&tv, 0) != 0) {
814+ fatal_abort("gettimeofday");
815+ }
816+ return static_cast<double>(tv.tv_usec) / 1000000 + tv.tv_sec;
817+}
818+
819+// unused
820+void
821+wait_close(int fd)
822+{
823+ char buf[1024];
824+ while (true) {
825+ int r = read(fd, buf, sizeof(buf));
826+ if (r <= 0) {
827+ break;
828+ }
829+ }
830+}
831+
832+// unused
833+void
834+gentle_close(int fd)
835+{
836+ int r = shutdown(fd, SHUT_WR);
837+ if (r != 0) {
838+ return;
839+ }
840+ wait_close(fd);
841+}
842+
843+};
844+
845+struct hstest_shared {
846+ config conf;
847+ socket_args arg;
848+ int verbose;
849+ size_t loop;
850+ size_t pipe;
851+ char op;
852+ long num_threads;
853+ mutable volatile long count;
854+ mutable volatile long conn_count;
855+ long wait_conn;
856+ volatile char *keygen;
857+ long keygen_size;
858+ mutable volatile int enable_timing;
859+ int usleep;
860+ int dump;
861+ hstest_shared() : verbose(0), loop(0), pipe(0), op('G'), num_threads(0),
862+ count(0), conn_count(0), wait_conn(0), keygen(0), keygen_size(0),
863+ enable_timing(0), usleep(0), dump(0) { }
864+ void increment_count(unsigned int c = 1) const volatile {
865+ atomic_exchange_and_add(&count, c);
866+ }
867+ void increment_conn(unsigned int c) const volatile {
868+ atomic_exchange_and_add(&conn_count, c);
869+ while (wait_conn != 0 && conn_count < wait_conn) {
870+ sleep(1);
871+ }
872+ // fprintf(stderr, "wait_conn=%ld done\n", wait_conn);
873+ }
874+};
875+
876+struct hstest_thread {
877+ struct arg_type {
878+ size_t id;
879+ const hstest_shared& sh;
880+ bool watch_flag;
881+ arg_type(size_t i, const hstest_shared& s, bool w)
882+ : id(i), sh(s), watch_flag(w) { }
883+ };
884+ hstest_thread(const arg_type& a) : arg(a), io_success_count(0),
885+ op_success_count(0), response_min(99999), response_max(0),
886+ response_sum(0), response_avg(0) { }
887+ void operator ()();
888+ void test_1();
889+ void test_2_3(int test_num);
890+ void test_4_5(int test_num);
891+ void test_6(int test_num);
892+ void test_7(int test_num);
893+ void test_8(int test_num);
894+ void test_9(int test_num);
895+ void test_10(int test_num);
896+ void test_11(int test_num);
897+ void test_12(int test_num);
898+ void test_21(int test_num);
899+ void test_22(int test_num);
900+ void test_watch();
901+ void sleep_if();
902+ void set_timing(double time_spent);
903+ arg_type arg;
904+ auto_file fd;
905+ size_t io_success_count;
906+ size_t op_success_count;
907+ double response_min, response_max, response_sum, response_avg;
908+};
909+
910+void
911+hstest_thread::test_1()
912+{
913+ char buf[1024];
914+ unsigned int seed = arg.id;
915+ seed ^= arg.sh.conf.get_int("seed_xor", 0);
916+ std::string err;
917+ if (socket_connect(fd, arg.sh.arg, err) != 0) {
918+ fprintf(stderr, "connect: %d %s\n", errno, strerror(errno));
919+ return;
920+ }
921+ const char op = arg.sh.op;
922+ const int tablesize = arg.sh.conf.get_int("tablesize", 0);
923+ for (size_t i = 0; i < arg.sh.loop; ++i) {
924+ for (size_t j = 0; j < arg.sh.pipe; ++j) {
925+ int k = 0, v = 0, len = 0;
926+ if (op == 'G') {
927+ k = rand_r(&seed);
928+ v = rand_r(&seed); /* unused */
929+ if (tablesize != 0) {
930+ k &= tablesize;
931+ }
932+ len = snprintf(buf, sizeof(buf), "%c\tk%d\n", op, k);
933+ } else {
934+ k = rand_r(&seed);
935+ v = rand_r(&seed);
936+ if (tablesize != 0) {
937+ k &= tablesize;
938+ }
939+ len = snprintf(buf, sizeof(buf), "%c\tk%d\tv%d\n", op, k, v);
940+ }
941+ const int wlen = write(fd.get(), buf, len);
942+ if (wlen != len) {
943+ return;
944+ }
945+ }
946+ size_t read_cnt = 0;
947+ size_t read_pos = 0;
948+ while (read_cnt < arg.sh.pipe) {
949+ const int rlen = read(fd.get(), buf + read_pos, sizeof(buf) - read_pos);
950+ if (rlen <= 0) {
951+ return;
952+ }
953+ read_pos += rlen;
954+ while (true) {
955+ const char *const p = static_cast<const char *>(memchr(buf, '\n',
956+ read_pos));
957+ if (p == 0) {
958+ break;
959+ }
960+ ++read_cnt;
961+ ++io_success_count;
962+ arg.sh.increment_count();
963+ if (p != buf && buf[0] == '=') {
964+ ++op_success_count;
965+ }
966+ const size_t rest_size = buf + read_pos - (p + 1);
967+ if (rest_size != 0) {
968+ memmove(buf, p + 1, rest_size);
969+ }
970+ read_pos = rest_size;
971+ }
972+ }
973+ }
974+}
975+
976+void
977+hstest_thread::test_2_3(int test_num)
978+{
979+#if 0
980+ char buf_k[128], buf_v[128];
981+ unsigned int seed = arg.id;
982+ op_base_t op = static_cast<op_base_t>(arg.sh.op);
983+ micli_ptr hnd;
984+ if (test_num == 2) {
985+ hnd = micli_i::create_remote(arg.sh.conf);
986+ } else if (test_num == 3) {
987+ // hnd = micli_i::create_inproc(arg.sh.localdb);
988+ }
989+ if (hnd.get() == 0) {
990+ return;
991+ }
992+ for (size_t i = 0; i < arg.sh.loop; ++i) {
993+ for (size_t j = 0; j < arg.sh.pipe; ++j) {
994+ int k = 0, v = 0, klen = 0, vlen = 0;
995+ k = rand_r(&seed);
996+ klen = snprintf(buf_k, sizeof(buf_k), "k%d", k);
997+ v = rand_r(&seed); /* unused */
998+ vlen = snprintf(buf_v, sizeof(buf_v), "v%d", v);
999+ string_ref arr[2];
1000+ arr[0] = string_ref(buf_k, klen);
1001+ arr[1] = string_ref(buf_v, vlen);
1002+ pstrarr_ptr rec(arr, 2);
1003+ if (hnd->execute(op, 0, 0, rec.get_const())) {
1004+ ++io_success_count;
1005+ arg.sh.increment_count();
1006+ const dataset& res = hnd->get_result_ref();
1007+ if (res.size() == 1) {
1008+ ++op_success_count;
1009+ }
1010+ }
1011+ }
1012+ }
1013+#endif
1014+}
1015+
1016+void
1017+hstest_thread::test_4_5(int test_num)
1018+{
1019+#if 0
1020+ char buf_k[128], buf_v[8192];
1021+ memset(buf_v, ' ', sizeof(buf_v));
1022+ unsigned int seed = arg.id;
1023+ op_base_t op = static_cast<op_base_t>(arg.sh.op);
1024+ micli_ptr hnd;
1025+ if (test_num == 4) {
1026+ hnd = micli_i::create_remote(arg.sh.conf);
1027+ } else if (test_num == 5) {
1028+ hnd = micli_i::create_inproc(arg.sh.localdb);
1029+ }
1030+ if (hnd.get() == 0) {
1031+ return;
1032+ }
1033+ for (size_t i = 0; i < arg.sh.loop; ++i) {
1034+ for (size_t j = 0; j < arg.sh.pipe; ++j) {
1035+ int k = 0, klen = 0, vlen = 0;
1036+ k = i & 0x0000ffffUL;
1037+ if (k == 0) {
1038+ fprintf(stderr, "k=0\n");
1039+ }
1040+ klen = snprintf(buf_k, sizeof(buf_k), "k%d", k);
1041+ vlen = rand_r(&seed) % 8192;
1042+ string_ref arr[2];
1043+ arr[0] = string_ref(buf_k, klen);
1044+ arr[1] = string_ref(buf_v, vlen);
1045+ pstrarr_ptr rec(arr, 2);
1046+ if (hnd->execute(op, 0, 0, rec.get_const())) {
1047+ ++io_success_count;
1048+ const dataset& res = hnd->get_result_ref();
1049+ if (res.size() == 1) {
1050+ ++op_success_count;
1051+ }
1052+ }
1053+ }
1054+ }
1055+#endif
1056+}
1057+
1058+void
1059+hstest_thread::test_6(int test_num)
1060+{
1061+ int count = arg.sh.conf.get_int("count", 1);
1062+ auto_file fds[count];
1063+ for (int i = 0; i < count; ++i) {
1064+ const double t1 = gettimeofday_double();
1065+ std::string err;
1066+ if (socket_connect(fds[i], arg.sh.arg, err) != 0) {
1067+ fprintf(stderr, "id=%zu i=%d err=%s\n", arg.id, i, err.c_str());
1068+ }
1069+ const double t2 = gettimeofday_double();
1070+ if (t2 - t1 > 1) {
1071+ fprintf(stderr, "id=%zu i=%d time %f\n", arg.id, i, t2 - t1);
1072+ }
1073+ }
1074+}
1075+
1076+void
1077+hstest_thread::test_7(int num)
1078+{
1079+ /*
1080+ set foo 0 0 10
1081+ 0123456789
1082+ STORED
1083+ get foo
1084+ VALUE foo 0 10
1085+ 0123456789
1086+ END
1087+ get var
1088+ END
1089+ */
1090+ char buf[1024];
1091+ const int keep_connection = arg.sh.conf.get_int("keep_connection", 1);
1092+ unsigned int seed = arg.id;
1093+ seed ^= arg.sh.conf.get_int("seed_xor", 0);
1094+ const int tablesize = arg.sh.conf.get_int("tablesize", 0);
1095+ const char op = arg.sh.op;
1096+ for (size_t i = 0; i < arg.sh.loop; ++i) {
1097+ const double tm1 = gettimeofday_double();
1098+ std::string err;
1099+ if (fd.get() < 0 && socket_connect(fd, arg.sh.arg, err) != 0) {
1100+ fprintf(stderr, "connect: %d %s\n", errno, strerror(errno));
1101+ return;
1102+ }
1103+ for (size_t j = 0; j < arg.sh.pipe; ++j) {
1104+ int k = 0, v = 0, len = 0;
1105+ if (op == 'G') {
1106+ k = rand_r(&seed);
1107+ v = rand_r(&seed); /* unused */
1108+ if (tablesize != 0) {
1109+ k &= tablesize;
1110+ }
1111+ len = snprintf(buf, sizeof(buf), "get k%d\r\n", k);
1112+ } else {
1113+ k = rand_r(&seed);
1114+ v = rand_r(&seed);
1115+ if (tablesize != 0) {
1116+ k &= tablesize;
1117+ }
1118+ char vbuf[1024];
1119+ int vlen = snprintf(vbuf, sizeof(vbuf),
1120+ "v%d"
1121+ // "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
1122+ // "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
1123+ // "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
1124+ // "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
1125+ // "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
1126+ , v);
1127+ len = snprintf(buf, sizeof(buf), "set k%d 0 0 %d\r\n%s\r\n",
1128+ k, vlen, vbuf);
1129+ }
1130+ const int wlen = write(fd.get(), buf, len);
1131+ if (wlen != len) {
1132+ return;
1133+ }
1134+ }
1135+ size_t read_cnt = 0;
1136+ size_t read_pos = 0;
1137+ bool read_response_done = false;
1138+ bool expect_value = false;
1139+ while (!read_response_done) {
1140+ const int rlen = read(fd.get(), buf + read_pos, sizeof(buf) - read_pos);
1141+ if (rlen <= 0) {
1142+ return;
1143+ }
1144+ read_pos += rlen;
1145+ while (true) {
1146+ const char *const p = static_cast<const char *>(memchr(buf, '\n',
1147+ read_pos));
1148+ if (p == 0) {
1149+ break;
1150+ }
1151+ ++read_cnt;
1152+ if (expect_value) {
1153+ expect_value = false;
1154+ } else if (p >= buf + 6 && memcmp(buf, "VALUE ", 6) == 0) {
1155+ expect_value = true;
1156+ ++op_success_count;
1157+ } else {
1158+ if (p == buf + 7 && memcmp(buf, "STORED\r", 7) == 0) {
1159+ ++op_success_count;
1160+ }
1161+ read_response_done = true;
1162+ }
1163+ const size_t rest_size = buf + read_pos - (p + 1);
1164+ if (rest_size != 0) {
1165+ memmove(buf, p + 1, rest_size);
1166+ }
1167+ read_pos = rest_size;
1168+ }
1169+ ++io_success_count;
1170+ }
1171+ arg.sh.increment_count();
1172+ if (!keep_connection) {
1173+ fd.close();
1174+ }
1175+ const double tm2 = gettimeofday_double();
1176+ set_timing(tm2 - tm1);
1177+ sleep_if();
1178+ }
1179+}
1180+
1181+struct rec {
1182+ std::string key;
1183+ std::string value;
1184+};
1185+
1186+void
1187+hstest_thread::test_8(int test_num)
1188+{
1189+#if 0
1190+ char buf_k[128], buf_v[128];
1191+ unsigned int seed = arg.id;
1192+ // op_base_t op = static_cast<op_base_t>(arg.sh.op);
1193+ using namespace boost::multi_index;
1194+ typedef member<rec, std::string, &rec::key> rec_get_key;
1195+ typedef ordered_unique<rec_get_key> oui;
1196+ typedef multi_index_container< rec, indexed_by<oui> > mic;
1197+ #if 0
1198+ typedef std::map<std::string, std::string> m_type;
1199+ m_type m;
1200+ #endif
1201+ mic m;
1202+ for (size_t i = 0; i < arg.sh.loop; ++i) {
1203+ for (size_t j = 0; j < arg.sh.pipe; ++j) {
1204+ int k = 0, v = 0, klen = 0, vlen = 0;
1205+ k = rand_r(&seed);
1206+ klen = snprintf(buf_k, sizeof(buf_k), "k%d", k);
1207+ v = rand_r(&seed); /* unused */
1208+ vlen = snprintf(buf_v, sizeof(buf_v), "v%d", v);
1209+ const std::string ks(buf_k, klen);
1210+ const std::string vs(buf_v, vlen);
1211+ rec r;
1212+ r.key = ks;
1213+ r.value = vs;
1214+ m.insert(r);
1215+ // m.insert(std::make_pair(ks, vs));
1216+ ++io_success_count;
1217+ ++op_success_count;
1218+ arg.sh.increment_count();
1219+ }
1220+ }
1221+#endif
1222+}
1223+
1224+struct mysqltest_thread_initobj : private noncopyable {
1225+ mysqltest_thread_initobj() {
1226+ mysql_thread_init();
1227+ }
1228+ ~mysqltest_thread_initobj() {
1229+ mysql_thread_end();
1230+ }
1231+};
1232+
1233+void
1234+hstest_thread::test_9(int test_num)
1235+{
1236+ /* create table hstest
1237+ * ( k varchar(255) not null, v varchar(255) not null, primary key(k))
1238+ * engine = innodb; */
1239+ auto_mysql db;
1240+ // mysqltest_thread_initobj initobj;
1241+ std::string err;
1242+ const char op = arg.sh.op;
1243+ const std::string suffix = arg.sh.conf.get_str("value_suffix", "upd");
1244+ unsigned long long err_cnt = 0;
1245+ unsigned long long query_cnt = 0;
1246+ #if 0
1247+ my_bool reconnect = 0;
1248+ if (mysql_options(db, MYSQL_OPT_RECONNECT, &reconnect) != 0) {
1249+ err = "mysql_options() failed";
1250+ ++err_cnt;
1251+ return;
1252+ }
1253+ #endif
1254+ unsigned int seed = time(0) + arg.id + 1;
1255+ seed ^= arg.sh.conf.get_int("seed_xor", 0);
1256+ drand48_data randbuf;
1257+ srand48_r(seed, &randbuf);
1258+ const std::string mysql_host = arg.sh.conf.get_str("host", "localhost");
1259+ const int mysql_port = arg.sh.conf.get_int("mysqlport", 3306);
1260+ const int num = arg.sh.loop;
1261+ const std::string mysql_user = arg.sh.conf.get_str("mysqluser", "root");
1262+ const std::string mysql_passwd = arg.sh.conf.get_str("mysqlpass", "");
1263+ const std::string mysql_dbname = arg.sh.conf.get_str("dbname", "hstest");
1264+ const int keep_connection = arg.sh.conf.get_int("keep_connection", 1);
1265+ const int verbose = arg.sh.conf.get_int("verbose", 1);
1266+ const int tablesize = arg.sh.conf.get_int("tablesize", 10000);
1267+ const int moreflds = arg.sh.conf.get_int("moreflds", 0);
1268+ const std::string moreflds_prefix = arg.sh.conf.get_str(
1269+ "moreflds_prefix", "column0123456789_");
1270+ const int use_handler = arg.sh.conf.get_int("handler", 0);
1271+ const int sched_flag = arg.sh.conf.get_int("sched", 0);
1272+ const int ssps = arg.sh.conf.get_int("ssps", 0);
1273+ std::string flds = "v";
1274+ for (int i = 0; i < moreflds; ++i) {
1275+ char buf[1024];
1276+ snprintf(buf, sizeof(buf), ",%s%d", moreflds_prefix.c_str(), i);
1277+ flds += std::string(buf);
1278+ }
1279+ int connected = 0;
1280+ std::auto_ptr<auto_mysql_stmt> stmt;
1281+ for (int i = 0; i < num; ++i) {
1282+ const double tm1 = gettimeofday_double();
1283+ const int flags = 0;
1284+ if (connected == 0) {
1285+ if (!mysql_real_connect(db, mysql_host.c_str(),
1286+ mysql_user.c_str(), mysql_user.empty() ? 0 : mysql_passwd.c_str(),
1287+ mysql_dbname.c_str(), mysql_port, 0, flags)) {
1288+ err = "failed to connect: " + std::string(mysql_error(db));
1289+ if (verbose >= 1) {
1290+ fprintf(stderr, "e=[%s]\n", err.c_str());
1291+ }
1292+ ++err_cnt;
1293+ return;
1294+ }
1295+ arg.sh.increment_conn(1);
1296+ }
1297+ int r = 0;
1298+ if (connected == 0 && use_handler) {
1299+ const char *const q = "handler hstest_table1 open";
1300+ r = mysql_real_query(db, q, strlen(q));
1301+ if (r != 0) {
1302+ err = 1;
1303+ }
1304+ }
1305+ if (connected == 0 && ssps) {
1306+ stmt.reset(new auto_mysql_stmt(db));
1307+ const char *const q = "select v from hstest_table1 where k = ?";
1308+ r = mysql_stmt_prepare(*stmt, q, strlen(q));
1309+ if (r != 0) {
1310+ fprintf(stderr, "ssps err\n");
1311+ ++err_cnt;
1312+ return;
1313+ }
1314+ }
1315+ connected = 1;
1316+ std::string result_str;
1317+ unsigned int err = 0;
1318+ unsigned int num_flds = 0, num_affected_rows = 0;
1319+ int got_data = 0;
1320+ char buf_query[16384];
1321+ int buf_query_len = 0;
1322+ int k = 0, v = 0;
1323+ {
1324+ double kf = 0, vf = 0;
1325+ drand48_r(&randbuf, &kf);
1326+ drand48_r(&randbuf, &vf);
1327+ k = int(kf * tablesize);
1328+ v = int(vf * tablesize);
1329+ #if 0
1330+ k = rand_r(&seed);
1331+ v = rand_r(&seed);
1332+ if (tablesize != 0) {
1333+ k %= tablesize;
1334+ }
1335+ #endif
1336+ if (op == 'G') {
1337+ if (use_handler) {
1338+ buf_query_len = snprintf(buf_query, sizeof(buf_query),
1339+ "handler hstest_table1 read `primary` = ( '%d' )", k);
1340+ // TODO: moreflds
1341+ } else if (ssps) {
1342+ //
1343+ } else {
1344+ buf_query_len = snprintf(buf_query, sizeof(buf_query),
1345+ "select %s from hstest_table1 where k = '%d'", flds.c_str(), k);
1346+ }
1347+ } else if (op == 'U') {
1348+ buf_query_len = snprintf(buf_query, sizeof(buf_query),
1349+ "update hstest_table1 set v = '%d_%d%s' where k = '%d'",
1350+ v, k, suffix.c_str(), k);
1351+ } else if (op == 'R') {
1352+ buf_query_len = snprintf(buf_query, sizeof(buf_query),
1353+ "replace into hstest_table1 values ('%d', 'v%d')", k, v);
1354+ // TODO: moreflds
1355+ }
1356+ }
1357+ if (r == 0) {
1358+ if (ssps) {
1359+ MYSQL_BIND bind[1] = { };
1360+ bind[0].buffer_type = MYSQL_TYPE_LONG;
1361+ bind[0].buffer = (char *)&k;
1362+ bind[0].is_null = 0;
1363+ bind[0].length = 0;
1364+ if (mysql_stmt_bind_param(*stmt, bind)) {
1365+ fprintf(stderr, "err: %s\n", mysql_stmt_error(*stmt));
1366+ ++err_cnt;
1367+ return;
1368+ }
1369+ r = mysql_stmt_execute(*stmt);
1370+ // fprintf(stderr, "stmt exec\n");
1371+ } else {
1372+ r = mysql_real_query(db, buf_query, buf_query_len);
1373+ // fprintf(stderr, "real query\n");
1374+ }
1375+ ++query_cnt;
1376+ }
1377+ if (r != 0) {
1378+ err = 1;
1379+ } else if (ssps) {
1380+ if (verbose >= 0) {
1381+ char resbuf[1024];
1382+ unsigned long res_len = 0;
1383+ MYSQL_BIND bind[1] = { };
1384+ bind[0].buffer_type = MYSQL_TYPE_STRING;
1385+ bind[0].buffer = resbuf;
1386+ bind[0].buffer_length = sizeof(resbuf);
1387+ bind[0].length = &res_len;
1388+ if (mysql_stmt_bind_result(*stmt, bind)) {
1389+ fprintf(stderr, "err: %s\n", mysql_stmt_error(*stmt));
1390+ ++err_cnt;
1391+ return;
1392+ }
1393+ if (mysql_stmt_fetch(*stmt)) {
1394+ fprintf(stderr, "err: %s\n", mysql_stmt_error(*stmt));
1395+ ++err_cnt;
1396+ return;
1397+ }
1398+ if (!result_str.empty()) {
1399+ result_str += " ";
1400+ }
1401+ result_str += std::string(resbuf, res_len);
1402+ // fprintf(stderr, "SSPS RES: %s\n", result_str.c_str());
1403+ got_data = 1;
1404+ } else {
1405+ got_data = 1;
1406+ }
1407+ } else {
1408+ auto_mysql_res res(db);
1409+ if (res != 0) {
1410+ if (verbose >= 0) {
1411+ num_flds = mysql_num_fields(res);
1412+ MYSQL_ROW row = 0;
1413+ while ((row = mysql_fetch_row(res)) != 0) {
1414+ got_data = 1;
1415+ unsigned long *const lengths = mysql_fetch_lengths(res);
1416+ if (verbose >= 2) {
1417+ for (unsigned int i = 0; i < num_flds; ++i) {
1418+ if (!result_str.empty()) {
1419+ result_str += " ";
1420+ }
1421+ result_str += std::string(row[i], lengths[i]);
1422+ }
1423+ }
1424+ }
1425+ } else {
1426+ got_data = 1;
1427+ }
1428+ } else {
1429+ if (mysql_field_count(db) == 0) {
1430+ num_affected_rows = mysql_affected_rows(db);
1431+ } else {
1432+ err = 1;
1433+ }
1434+ }
1435+ }
1436+ if (verbose >= 2 || (verbose >= 1 && err != 0)) {
1437+ if (err) {
1438+ ++err_cnt;
1439+ const char *const errstr = mysql_error(db);
1440+ fprintf(stderr, "e=[%s] a=%u q=[%s]\n", errstr,
1441+ num_affected_rows, buf_query);
1442+ } else {
1443+ fprintf(stderr, "a=%u q=[%s] r=[%s]\n", num_affected_rows, buf_query,
1444+ result_str.c_str());
1445+ }
1446+ }
1447+ if (err == 0) {
1448+ ++io_success_count;
1449+ if (num_affected_rows > 0 || got_data > 0) {
1450+ ++op_success_count;
1451+ } else {
1452+ if (verbose >= 1) {
1453+ fprintf(stderr, "k=%d numaff=%u gotdata=%d\n",
1454+ k, num_affected_rows, got_data);
1455+ }
1456+ }
1457+ arg.sh.increment_count();
1458+ }
1459+ if (!keep_connection) {
1460+ if (stmt.get() != 0) {
1461+ stmt.reset();
1462+ }
1463+ db.reset();
1464+ connected = 0;
1465+ }
1466+ const double tm2 = gettimeofday_double();
1467+ set_timing(tm2 - tm1);
1468+ sleep_if();
1469+ if (sched_flag) {
1470+ sched_yield();
1471+ }
1472+ }
1473+ if (verbose >= 1) {
1474+ fprintf(stderr, "thread finished (error_count=%llu)\n", err_cnt);
1475+ }
1476+}
1477+
1478+void
1479+hstest_thread::test_10(int test_num)
1480+{
1481+ const int keep_connection = arg.sh.conf.get_int("keep_connection", 1);
1482+ unsigned int seed = time(0) + arg.id + 1;
1483+ seed ^= arg.sh.conf.get_int("seed_xor", 0);
1484+ drand48_data randbuf;
1485+ srand48_r(seed, &randbuf);
1486+ std::string err;
1487+ int keepconn_count = 0;
1488+ const char op = arg.sh.op;
1489+ const int verbose = arg.sh.conf.get_int("verbose", 1);
1490+ const std::string suffix = arg.sh.conf.get_str("value_suffix", "upd");
1491+ const int tablesize = arg.sh.conf.get_int("tablesize", 10000);
1492+ const int firstkey = arg.sh.conf.get_int("firstkey", 0);
1493+ const int sched_flag = arg.sh.conf.get_int("sched", 0);
1494+ const int moreflds = arg.sh.conf.get_int("moreflds", 0);
1495+ const std::string dbname = arg.sh.conf.get_str("dbname", "hstest");
1496+ const std::string table = arg.sh.conf.get_str("table", "hstest_table1");
1497+ const std::string index = arg.sh.conf.get_str("index", "PRIMARY");
1498+ const std::string field = arg.sh.conf.get_str("field", "v");
1499+ const std::string moreflds_prefix = arg.sh.conf.get_str(
1500+ "moreflds_prefix", "column0123456789_");
1501+ const int dump = arg.sh.dump;
1502+ const int nodup = arg.sh.conf.get_int("nodup", 0);
1503+ std::string moreflds_str;
1504+ for (int i = 0; i < moreflds; ++i) {
1505+ char sbuf[1024];
1506+ snprintf(sbuf, sizeof(sbuf), ",%s%d", moreflds_prefix.c_str(), i);
1507+ moreflds_str += std::string(sbuf);
1508+ }
1509+ char wbuf[16384], rbuf[16384];
1510+ int wbuflen = 0;
1511+ for (size_t i = 0; i < arg.sh.loop; ++i) {
1512+ int len = 0, rlen = 0, wlen = 0;
1513+ #if 0
1514+ const double tm1 = gettimeofday_double();
1515+ #endif
1516+ if (fd.get() < 0) {
1517+ if (socket_connect(fd, arg.sh.arg, err) != 0) {
1518+ fprintf(stderr, "connect: %d %s\n", errno, strerror(errno));
1519+ return;
1520+ }
1521+ len = snprintf(wbuf, sizeof(wbuf),
1522+ "P\t1\t%s\t%s\tPRIMARY\t%s%s\n", dbname.c_str(), table.c_str(),
1523+ field.c_str(), moreflds_str.c_str());
1524+ /* pst_num, db, table, index, retflds */
1525+ wlen = write(fd.get(), wbuf, len);
1526+ if (len != wlen) {
1527+ fprintf(stderr, "write: %d %d\n", len, wlen);
1528+ return;
1529+ }
1530+ rlen = read(fd.get(), rbuf, sizeof(rbuf));
1531+ if (rlen <= 0 || rbuf[rlen - 1] != '\n') {
1532+ fprintf(stderr, "read: rlen=%d errno=%d\n", rlen, errno);
1533+ return;
1534+ }
1535+ if (rbuf[0] != '0') {
1536+ fprintf(stderr, "failed to open table\n");
1537+ return;
1538+ }
1539+ arg.sh.increment_conn(1);
1540+ }
1541+ const double tm1 = gettimeofday_double();
1542+ for (size_t j = 0; j < arg.sh.pipe; ++j) {
1543+ int k = 0, v = 0;
1544+ wbuflen = 0;
1545+ {
1546+ while (true) {
1547+ double kf = 0, vf = 0;
1548+ drand48_r(&randbuf, &kf);
1549+ drand48_r(&randbuf, &vf);
1550+ k = int(kf * tablesize) + firstkey;
1551+ v = int(vf * tablesize) + firstkey;
1552+ // k = rand_r(&seed);
1553+ // v = rand_r(&seed); /* unused */
1554+ #if 0
1555+ if (tablesize != 0) {
1556+ k &= tablesize;
1557+ }
1558+ #endif
1559+ if (op == 'G') {
1560+ wbuflen = snprintf(wbuf, sizeof(wbuf), "1\t=\t1\t%d\n", k);
1561+ } else if (op == 'U') {
1562+ wbuflen = snprintf(wbuf, sizeof(wbuf),
1563+ "1\t=\t1\t%d\t1\t0\tU\t%d_%d%s\n", k, v, k, suffix.c_str());
1564+ }
1565+ if (k - firstkey < arg.sh.keygen_size) {
1566+ volatile char *const ptr = arg.sh.keygen + (k - firstkey);
1567+ // int oldv = __sync_fetch_and_or(ptr, 1);
1568+ int oldv = *ptr;
1569+ *ptr += 1;
1570+ if (nodup && oldv != 0) {
1571+ if (dump) {
1572+ fprintf(stderr, "retry\n");
1573+ }
1574+ continue;
1575+ }
1576+ } else {
1577+ if (nodup) {
1578+ if (dump) {
1579+ fprintf(stderr, "retry2\n");
1580+ }
1581+ continue;
1582+ }
1583+ }
1584+ break;
1585+ }
1586+ }
1587+ wlen = write(fd.get(), wbuf, wbuflen);
1588+ if (wlen != wbuflen) {
1589+ fprintf(stderr, "write: %d %d\n", wbuflen, wlen);
1590+ return;
1591+ }
1592+ }
1593+ size_t read_cnt = 0;
1594+ size_t read_pos = 0;
1595+ while (read_cnt < arg.sh.pipe) {
1596+ rlen = read(fd.get(), rbuf + read_pos, sizeof(rbuf) - read_pos);
1597+ if (rlen <= 0) {
1598+ fprintf(stderr, "read: %d\n", rlen);
1599+ return;
1600+ }
1601+ read_pos += rlen;
1602+ while (true) {
1603+ const char *const nl = static_cast<const char *>(memchr(rbuf, '\n',
1604+ read_pos));
1605+ if (nl == 0) {
1606+ break;
1607+ }
1608+ ++read_cnt;
1609+ ++io_success_count;
1610+ const char *t1 = static_cast<const char *>(memchr(rbuf, '\t',
1611+ nl - rbuf));
1612+ if (t1 == 0) {
1613+ fprintf(stderr, "error \n");
1614+ break;
1615+ }
1616+ ++t1;
1617+ const char *t2 = static_cast<const char *>(memchr(t1, '\t',
1618+ nl - t1));
1619+ if (t2 == 0) {
1620+ if (verbose > 1) {
1621+ fprintf(stderr, "key: notfound \n");
1622+ }
1623+ break;
1624+ }
1625+ ++t2;
1626+ if (t1 == rbuf + 2 && rbuf[0] == '0') {
1627+ if (op == 'G') {
1628+ ++op_success_count;
1629+ arg.sh.increment_count();
1630+ } else if (op == 'U') {
1631+ const char *t3 = t2;
1632+ while (t3 != nl && t3[0] >= 0x10) {
1633+ ++t3;
1634+ }
1635+ if (t3 != t2 + 1 || t2[0] != '1') {
1636+ const std::string mess(t2, t3);
1637+ fprintf(stderr, "mod: %s\n", mess.c_str());
1638+ } else {
1639+ ++op_success_count;
1640+ arg.sh.increment_count();
1641+ if (arg.sh.dump && arg.sh.pipe == 1) {
1642+ fwrite(wbuf, wbuflen, 1, stderr);
1643+ }
1644+ }
1645+ }
1646+ } else {
1647+ const char *t3 = t2;
1648+ while (t3 != nl && t3[0] >= 0x10) {
1649+ ++t3;
1650+ }
1651+ const std::string mess(t2, t3);
1652+ fprintf(stderr, "err: %s\n", mess.c_str());
1653+ }
1654+ const size_t rest_size = rbuf + read_pos - (nl + 1);
1655+ if (rest_size != 0) {
1656+ memmove(rbuf, nl + 1, rest_size);
1657+ }
1658+ read_pos = rest_size;
1659+ }
1660+ }
1661+ if (!keep_connection) {
1662+ fd.reset();
1663+ arg.sh.increment_conn(-1);
1664+ } else if (keep_connection > 1 && ++keepconn_count > keep_connection) {
1665+ keepconn_count = 0;
1666+ fd.reset();
1667+ arg.sh.increment_conn(-1);
1668+ }
1669+ const double tm2 = gettimeofday_double();
1670+ set_timing(tm2 - tm1);
1671+ sleep_if();
1672+ if (sched_flag) {
1673+ sched_yield();
1674+ }
1675+ }
1676+ if (dump) {
1677+ fprintf(stderr, "done\n");
1678+ }
1679+}
1680+
1681+void
1682+hstest_thread::sleep_if()
1683+{
1684+ if (arg.sh.usleep) {
1685+ struct timespec ts = {
1686+ arg.sh.usleep / 1000000,
1687+ (arg.sh.usleep % 1000000) * 1000
1688+ };
1689+ nanosleep(&ts, 0);
1690+ }
1691+}
1692+
1693+void
1694+hstest_thread::set_timing(double time_spent)
1695+{
1696+ response_min = std::min(response_min, time_spent);
1697+ response_max = std::max(response_max, time_spent);
1698+ response_sum += time_spent;
1699+ if (op_success_count != 0) {
1700+ response_avg = response_sum / op_success_count;
1701+ }
1702+}
1703+
1704+void
1705+hstest_thread::test_11(int test_num)
1706+{
1707+ const int keep_connection = arg.sh.conf.get_int("keep_connection", 1);
1708+ const int tablesize = arg.sh.conf.get_int("tablesize", 0);
1709+ unsigned int seed = arg.id;
1710+ seed ^= arg.sh.conf.get_int("seed_xor", 0);
1711+ std::string err;
1712+ hstcpcli_ptr cli;
1713+ for (size_t i = 0; i < arg.sh.loop; ++i) {
1714+ if (cli.get() == 0) {
1715+ cli = hstcpcli_i::create(arg.sh.arg);
1716+ cli->request_buf_open_index(0, "hstest", "hstest_table1", "", "v");
1717+ /* pst_num, db, table, index, retflds */
1718+ if (cli->request_send() != 0) {
1719+ fprintf(stderr, "reuqest_send: %s\n", cli->get_error().c_str());
1720+ return;
1721+ }
1722+ size_t num_flds = 0;
1723+ if (cli->response_recv(num_flds) != 0) {
1724+ fprintf(stderr, "reuqest_recv: %s\n", cli->get_error().c_str());
1725+ return;
1726+ }
1727+ cli->response_buf_remove();
1728+ }
1729+ for (size_t j = 0; j < arg.sh.pipe; ++j) {
1730+ char buf[256];
1731+ int k = 0, v = 0, len = 0;
1732+ {
1733+ k = rand_r(&seed);
1734+ v = rand_r(&seed); /* unused */
1735+ if (tablesize != 0) {
1736+ k &= tablesize;
1737+ }
1738+ len = snprintf(buf, sizeof(buf), "%d", k);
1739+ }
1740+ const string_ref key(buf, len);
1741+ const string_ref op("=", 1);
1742+ cli->request_buf_exec_generic(0, op, &key, 1, 1, 0, string_ref(), 0, 0);
1743+ }
1744+ if (cli->request_send() != 0) {
1745+ fprintf(stderr, "reuqest_send: %s\n", cli->get_error().c_str());
1746+ return;
1747+ }
1748+ size_t read_cnt = 0;
1749+ for (size_t j = 0; j < arg.sh.pipe; ++j) {
1750+ size_t num_flds = 0;
1751+ if (cli->response_recv(num_flds) != 0) {
1752+ fprintf(stderr, "reuqest_recv: %s\n", cli->get_error().c_str());
1753+ return;
1754+ }
1755+ {
1756+ ++read_cnt;
1757+ ++io_success_count;
1758+ arg.sh.increment_count();
1759+ {
1760+ ++op_success_count;
1761+ }
1762+ }
1763+ cli->response_buf_remove();
1764+ }
1765+ if (!keep_connection) {
1766+ cli.reset();
1767+ }
1768+ }
1769+}
1770+
1771+void
1772+hstest_thread::test_watch()
1773+{
1774+ const int timelimit = arg.sh.conf.get_int("timelimit", 0);
1775+ const int timelimit_offset = timelimit / 2;
1776+ int loop = 0;
1777+ double t1 = 0, t2 = 0;
1778+ size_t cnt_t1 = 0, cnt_t2 = 0;
1779+ size_t prev_cnt = 0;
1780+ double now_f = 0;
1781+ while (true) {
1782+ sleep(1);
1783+ const size_t cnt = arg.sh.count;
1784+ const size_t df = cnt - prev_cnt;
1785+ prev_cnt = cnt;
1786+ const double now_prev = now_f;
1787+ now_f = gettimeofday_double();
1788+ if (now_prev != 0) {
1789+ const double rps = static_cast<double>(df) / (now_f - now_prev);
1790+ fprintf(stderr, "now: %zu cntdiff: %zu tdiff: %f rps: %f\n",
1791+ static_cast<size_t>(now_f), df, now_f - now_prev, rps);
1792+ }
1793+ if (timelimit != 0) {
1794+ if (arg.sh.wait_conn == 0 || arg.sh.conn_count >= arg.sh.wait_conn) {
1795+ ++loop;
1796+ }
1797+ if (loop == timelimit_offset) {
1798+ t1 = gettimeofday_double();
1799+ cnt_t1 = cnt;
1800+ arg.sh.enable_timing = 1;
1801+ fprintf(stderr, "start timing\n");
1802+ } else if (loop == timelimit_offset + timelimit) {
1803+ t2 = gettimeofday_double();
1804+ cnt_t2 = cnt;
1805+ const size_t cnt_diff = cnt_t2 - cnt_t1;
1806+ const double tdiff = t2 - t1;
1807+ const double qps = cnt_diff / (tdiff != 0 ? tdiff : 1);
1808+ fprintf(stderr, "(%f: %zu, %f: %zu), %10.5f qps\n",
1809+ t1, cnt_t1, t2, cnt_t2, qps);
1810+ size_t keycnt = 0;
1811+ for (int i = 0; i < arg.sh.keygen_size; ++i) {
1812+ if (arg.sh.keygen[i]) {
1813+ ++keycnt;
1814+ }
1815+ }
1816+ fprintf(stderr, "keygen=%zu\n", keycnt);
1817+ break;
1818+ }
1819+ }
1820+ }
1821+#if 0
1822+ int loop = 0;
1823+ double t1 = 0, t2 = 0;
1824+ size_t cnt_t1 = 0, cnt_t2 = 0;
1825+ size_t prev_cnt = 0;
1826+ while (true) {
1827+ sleep(1);
1828+ const size_t cnt = arg.sh.count;
1829+ const size_t df = cnt - prev_cnt;
1830+ prev_cnt = cnt;
1831+ const size_t now = time(0);
1832+ fprintf(stderr, "%zu %zu\n", now, df);
1833+ if (timelimit != 0) {
1834+ ++loop;
1835+ if (loop == timelimit_offset) {
1836+ t1 = gettimeofday_double();
1837+ cnt_t1 = cnt;
1838+ } else if (loop == timelimit_offset + timelimit) {
1839+ t2 = gettimeofday_double();
1840+ cnt_t2 = cnt;
1841+ const size_t cnt_diff = cnt_t2 - cnt_t1;
1842+ const double tdiff = t2 - t1;
1843+ const double qps = cnt_diff / (tdiff != 0 ? tdiff : 1);
1844+ fprintf(stderr, "(%f: %zu, %f: %zu), %10.5f qps\n",
1845+ t1, cnt_t1, t2, cnt_t2, qps);
1846+ size_t keycnt = 0;
1847+ for (int i = 0; i < arg.sh.keygen_size; ++i) {
1848+ if (arg.sh.keygen[i]) {
1849+ ++keycnt;
1850+ }
1851+ }
1852+ fprintf(stderr, "keygen=%zu\n", keycnt);
1853+ _exit(0);
1854+ }
1855+ }
1856+ }
1857+#endif
1858+}
1859+
1860+void
1861+hstest_thread::test_12(int test_num)
1862+{
1863+ /* NOTE: num_threads should be 1 */
1864+ /* create table hstest
1865+ * ( k varchar(255) not null, v varchar(255) not null, primary key(k))
1866+ * engine = innodb; */
1867+ mysqltest_thread_initobj initobj;
1868+ auto_mysql db;
1869+ std::string err;
1870+ unsigned long long err_cnt = 0;
1871+ unsigned long long query_cnt = 0;
1872+ #if 0
1873+ my_bool reconnect = 0;
1874+ if (mysql_options(db, MYSQL_OPT_RECONNECT, &reconnect) != 0) {
1875+ err = "mysql_options() failed";
1876+ ++err_cnt;
1877+ return;
1878+ }
1879+ #endif
1880+ const std::string mysql_host = arg.sh.conf.get_str("host", "localhost");
1881+ const int mysql_port = arg.sh.conf.get_int("mysqlport", 3306);
1882+ const unsigned int num = arg.sh.loop;
1883+ const size_t pipe = arg.sh.pipe;
1884+ const std::string mysql_user = arg.sh.conf.get_str("mysqluser", "root");
1885+ const std::string mysql_passwd = arg.sh.conf.get_str("mysqlpass", "");
1886+ const std::string mysql_dbname = arg.sh.conf.get_str("db", "hstest");
1887+ const int keep_connection = arg.sh.conf.get_int("keep_connection", 1);
1888+ const int verbose = arg.sh.conf.get_int("verbose", 1);
1889+ const int use_handler = arg.sh.conf.get_int("handler", 0);
1890+ int connected = 0;
1891+ unsigned int k = 0;
1892+ string_buffer buf;
1893+ for (unsigned int i = 0; i < num; ++i) {
1894+ const int flags = 0;
1895+ if (connected == 0 && !mysql_real_connect(db, mysql_host.c_str(),
1896+ mysql_user.c_str(), mysql_user.empty() ? 0 : mysql_passwd.c_str(),
1897+ mysql_dbname.c_str(), mysql_port, 0, flags)) {
1898+ err = "failed to connect: " + std::string(mysql_error(db));
1899+ if (verbose >= 1) {
1900+ fprintf(stderr, "e=[%s]\n", err.c_str());
1901+ }
1902+ ++err_cnt;
1903+ return;
1904+ }
1905+ int r = 0;
1906+ if (connected == 0 && use_handler) {
1907+ const char *const q = "handler hstest open";
1908+ r = mysql_real_query(db, q, strlen(q));
1909+ if (r != 0) {
1910+ err = 1;
1911+ }
1912+ }
1913+ connected = 1;
1914+ std::string result_str;
1915+ unsigned int err = 0;
1916+ unsigned int num_flds = 0, num_affected_rows = 0;
1917+ int got_data = 0;
1918+ buf.clear();
1919+ buf.append_literal("insert into hstest values ");
1920+ for (size_t j = 0; j < pipe; ++j) {
1921+ const unsigned int v = ~k;
1922+ if (j != 0) {
1923+ buf.append_literal(",");
1924+ }
1925+ char *wp = buf.make_space(64);
1926+ int buf_query_len = snprintf(wp, 64, "('k%u', 'v%u')", k, v);
1927+ buf.space_wrote(buf_query_len);
1928+ ++k;
1929+ }
1930+ if (r == 0) {
1931+ r = mysql_real_query(db, buf.begin(), buf.size());
1932+ ++query_cnt;
1933+ }
1934+ if (r != 0) {
1935+ err = 1;
1936+ } else {
1937+ auto_mysql_res res(db);
1938+ if (res != 0) {
1939+ if (verbose >= 0) {
1940+ num_flds = mysql_num_fields(res);
1941+ MYSQL_ROW row = 0;
1942+ while ((row = mysql_fetch_row(res)) != 0) {
1943+ got_data = 1;
1944+ unsigned long *const lengths = mysql_fetch_lengths(res);
1945+ if (verbose >= 2) {
1946+ for (unsigned int i = 0; i < num_flds; ++i) {
1947+ if (!result_str.empty()) {
1948+ result_str += " ";
1949+ }
1950+ result_str += std::string(row[i], lengths[i]);
1951+ }
1952+ }
1953+ }
1954+ }
1955+ } else {
1956+ if (mysql_field_count(db) == 0) {
1957+ num_affected_rows = mysql_affected_rows(db);
1958+ } else {
1959+ err = 1;
1960+ }
1961+ }
1962+ }
1963+ if (verbose >= 2 || (verbose >= 1 && err != 0)) {
1964+ if (err) {
1965+ ++err_cnt;
1966+ const char *const errstr = mysql_error(db);
1967+ fprintf(stderr, "e=[%s] a=%u q=[%s]\n", errstr,
1968+ num_affected_rows, std::string(buf.begin(), buf.size()).c_str());
1969+ } else {
1970+ fprintf(stderr, "a=%u q=[%s] r=[%s]\n", num_affected_rows,
1971+ std::string(buf.begin(), buf.size()).c_str(),
1972+ result_str.c_str());
1973+ }
1974+ }
1975+ if (err == 0) {
1976+ ++io_success_count;
1977+ if (num_affected_rows > 0 || got_data > 0) {
1978+ ++op_success_count;
1979+ }
1980+ arg.sh.increment_count(pipe);
1981+ }
1982+ if (!keep_connection) {
1983+ db.reset();
1984+ connected = 0;
1985+ }
1986+ }
1987+ if (verbose >= 1) {
1988+ fprintf(stderr, "thread finished (error_count=%llu)\n", err_cnt);
1989+ }
1990+}
1991+
1992+void
1993+hstest_thread::test_21(int num)
1994+{
1995+ /* fsync test */
1996+ unsigned int id = arg.id;
1997+ std::string err;
1998+ #if 0
1999+ if (socket_connect(fd, arg.sh.arg, err) != 0) {
2000+ fprintf(stderr, "connect: %d %s\n", errno, strerror(errno));
2001+ return;
2002+ }
2003+ #endif
2004+ auto_file logfd;
2005+ char fname[1024];
2006+ snprintf(fname, sizeof(fname), "synctest_%u", id);
2007+ int open_flags = O_WRONLY | O_CREAT | O_TRUNC | O_APPEND;
2008+ logfd.reset(open(fname, open_flags, 0644));
2009+ if (logfd.get() < 0) {
2010+ fprintf(stderr, "open: %s: %d %s\n", fname, errno, strerror(errno));
2011+ return;
2012+ }
2013+ char buf[1024];
2014+ unsigned long long count = 0;
2015+ while (true) {
2016+ snprintf(buf, sizeof(buf), "%u %llu\n", id, count);
2017+ const size_t len = strlen(buf);
2018+ if (write(logfd.get(), buf, len) != (ssize_t)len) {
2019+ fprintf(stderr, "write: %s: %d %s\n", fname, errno, strerror(errno));
2020+ return;
2021+ }
2022+ #if 0
2023+ if (write(fd.get(), buf, len) != (ssize_t)len) {
2024+ fprintf(stderr, "write(sock): %d %s\n", errno, strerror(errno));
2025+ return;
2026+ }
2027+ #endif
2028+ if (fdatasync(logfd.get()) != 0) {
2029+ fprintf(stderr, "fsync: %s: %d %s\n", fname, errno, strerror(errno));
2030+ return;
2031+ }
2032+ ++count;
2033+ ++op_success_count;
2034+ arg.sh.increment_count();
2035+ }
2036+}
2037+
2038+void
2039+hstest_thread::test_22(int num)
2040+{
2041+ /* dd if=/dev/zero of=dummy.dat bs=1024M count=100 */
2042+ unsigned int id = arg.id;
2043+ std::string err;
2044+ auto_file filefd;
2045+ char fname[1024];
2046+ snprintf(fname, sizeof(fname), "dummy.dat");
2047+ int open_flags = O_RDONLY | O_DIRECT;
2048+ filefd.reset(open(fname, open_flags, 0644));
2049+ if (filefd.get() < 0) {
2050+ fprintf(stderr, "open: %s: %d %s\n", fname, errno, strerror(errno));
2051+ return;
2052+ }
2053+ char buf_x[4096 * 2];
2054+ char *const buf = (char *)(size_t(buf_x + 4096) / 4096 * 4096);
2055+ unsigned long long count = 0;
2056+ drand48_data randbuf;
2057+ unsigned long long seed = time(0);
2058+ seed *= 10;
2059+ seed += id;
2060+ srand48_r(seed, &randbuf);
2061+ for (unsigned int i = 0; i < arg.sh.loop; ++i) {
2062+ double kf = 0;
2063+ drand48_r(&randbuf, &kf);
2064+ kf *= (209715200 / 1);
2065+ // fprintf(stderr, "v=%f\n", kf);
2066+ off_t v = static_cast<off_t>(kf);
2067+ v %= (209715200 / 1);
2068+ v *= (512 * 1);
2069+ const double tm1 = gettimeofday_double();
2070+ const ssize_t r = pread(filefd.get(), buf, (512 * 1), v);
2071+ const double tm2 = gettimeofday_double();
2072+ if (r < 0) {
2073+ fprintf(stderr, "pread: %s: %d %s\n", fname, errno, strerror(errno));
2074+ return;
2075+ }
2076+ ++count;
2077+ ++op_success_count;
2078+ arg.sh.increment_count();
2079+ set_timing(tm2 - tm1);
2080+ }
2081+}
2082+
2083+void
2084+hstest_thread::operator ()()
2085+{
2086+ if (arg.watch_flag) {
2087+ return test_watch();
2088+ }
2089+ int test_num = arg.sh.conf.get_int("test", 1);
2090+ if (test_num == 1) {
2091+ test_1();
2092+ } else if (test_num == 2 || test_num == 3) {
2093+ test_2_3(test_num);
2094+ } else if (test_num == 4 || test_num == 5) {
2095+ test_4_5(test_num);
2096+ } else if (test_num == 6) {
2097+ test_6(test_num);
2098+ } else if (test_num == 7) {
2099+ test_7(test_num);
2100+ } else if (test_num == 8) {
2101+ test_8(test_num);
2102+ } else if (test_num == 9) {
2103+ test_9(test_num);
2104+ } else if (test_num == 10) {
2105+ test_10(test_num);
2106+ } else if (test_num == 11) {
2107+ test_11(test_num);
2108+ } else if (test_num == 12) {
2109+ test_12(test_num);
2110+ } else if (test_num == 21) {
2111+ test_21(test_num);
2112+ } else if (test_num == 22) {
2113+ test_22(test_num);
2114+ }
2115+ const int halt = arg.sh.conf.get_int("halt", 0);
2116+ if (halt) {
2117+ fprintf(stderr, "thread halted\n");
2118+ while (true) {
2119+ sleep(100000);
2120+ }
2121+ }
2122+ fprintf(stderr, "thread finished\n");
2123+}
2124+
2125+int
2126+hstest_main(int argc, char **argv)
2127+{
2128+ ignore_sigpipe();
2129+ hstest_shared shared;
2130+ parse_args(argc, argv, shared.conf);
2131+ shared.conf["port"] = shared.conf["hsport"];
2132+ shared.arg.set(shared.conf);
2133+ shared.loop = shared.conf.get_int("num", 1000);
2134+ shared.pipe = shared.conf.get_int("pipe", 1);
2135+ shared.verbose = shared.conf.get_int("verbose", 1);
2136+ const int tablesize = shared.conf.get_int("tablesize", 0);
2137+ std::vector<char> keygen(tablesize);
2138+ shared.keygen = &keygen[0];
2139+ shared.keygen_size = tablesize;
2140+ shared.usleep = shared.conf.get_int("usleep", 0);
2141+ shared.dump = shared.conf.get_int("dump", 0);
2142+ shared.num_threads = shared.conf.get_int("num_threads", 10);
2143+ shared.wait_conn = shared.conf.get_int("wait_conn", 0);
2144+ const std::string op = shared.conf.get_str("op", "G");
2145+ if (op.size() > 0) {
2146+ shared.op = op[0];
2147+ }
2148+ #if 0
2149+ const int localdb_flag = shared.conf.get_int("local", 0);
2150+ if (localdb_flag) {
2151+ shared.localdb = database_i::create(shared.conf);
2152+ }
2153+ #endif
2154+ const int num_thrs = shared.num_threads;
2155+ typedef thread<hstest_thread> thread_type;
2156+ typedef std::auto_ptr<thread_type> thread_ptr;
2157+ typedef auto_ptrcontainer< std::vector<thread_type *> > thrs_type;
2158+ thrs_type thrs;
2159+ for (int i = 0; i < num_thrs; ++i) {
2160+ const hstest_thread::arg_type arg(i, shared, false);
2161+ thread_ptr thr(new thread<hstest_thread>(arg));
2162+ thrs.push_back_ptr(thr);
2163+ }
2164+ for (size_t i = 0; i < thrs.size(); ++i) {
2165+ thrs[i]->start();
2166+ }
2167+ thread_ptr watch_thread;
2168+ const int timelimit = shared.conf.get_int("timelimit", 0);
2169+ {
2170+ const hstest_thread::arg_type arg(0, shared, true);
2171+ watch_thread = thread_ptr(new thread<hstest_thread>(arg));
2172+ watch_thread->start();
2173+ }
2174+ size_t iocnt = 0, opcnt = 0;
2175+ double respmin = 999999, respmax = 0;
2176+ double respsum = 0;
2177+ if (timelimit != 0) {
2178+ watch_thread->join();
2179+ }
2180+ for (size_t i = 0; i < thrs.size(); ++i) {
2181+ if (timelimit == 0) {
2182+ thrs[i]->join();
2183+ }
2184+ iocnt += (*thrs[i])->io_success_count;
2185+ opcnt += (*thrs[i])->op_success_count;
2186+ respmin = std::min(respmin, (*thrs[i])->response_min);
2187+ respmax = std::max(respmax, (*thrs[i])->response_max);
2188+ respsum += (*thrs[i])->response_sum;
2189+ }
2190+ fprintf(stderr, "io_success_count=%zu op_success_count=%zu\n", iocnt, opcnt);
2191+ fprintf(stderr, "respmin=%f respmax=%f respsum=%f respavg=%f\n",
2192+ respmin, respmax, respsum, respsum / opcnt);
2193+ size_t keycnt = 0;
2194+ for (size_t i = 0; i < keygen.size(); ++i) {
2195+ if (keygen[i]) {
2196+ ++keycnt;
2197+ }
2198+ }
2199+ fprintf(stderr, "keycnt=%zu\n", keycnt);
2200+ _exit(0);
2201+ return 0;
2202+}
2203+
2204+};
2205+
2206+int
2207+main(int argc, char **argv)
2208+{
2209+ return dena::hstest_main(argc, argv);
2210+}
2211+
2212
2213=== added file 'HandlerSocket-Plugin-for-MySQL/client/hstest.pl'
2214--- HandlerSocket-Plugin-for-MySQL/client/hstest.pl 1970-01-01 00:00:00 +0000
2215+++ HandlerSocket-Plugin-for-MySQL/client/hstest.pl 2011-04-12 04:16:24 +0000
2216@@ -0,0 +1,228 @@
2217+#!/usr/bin/perl
2218+
2219+# vim:sw=8:ai:ts=8
2220+
2221+use strict;
2222+use warnings;
2223+
2224+use DBI;
2225+use Net::HandlerSocket;
2226+
2227+my %conf = ();
2228+for my $i (@ARGV) {
2229+ my ($k, $v) = split(/=/, $i);
2230+ $conf{$k} = $v;
2231+}
2232+
2233+my $verbose = get_conf("verbose", 0);
2234+my $actions_str = get_conf("actions", "hsread");
2235+my $tablesize = get_conf("tablesize", 10000);
2236+my $db = get_conf("db", "hstest");
2237+my $table = get_conf("table", "hstest_table1");
2238+my $engine = get_conf("engine", "innodb");
2239+my $host = get_conf("host", "localhost");
2240+my $mysqlport = get_conf("mysqlport", 3306);
2241+my $mysqluser = get_conf("mysqluser", "root");
2242+my $mysqlpass = get_conf("mysqlpass", "");
2243+my $hsport = get_conf("hsport", 9999);
2244+my $loop = get_conf("loop", 10000);
2245+my $op = get_conf("op", "=");
2246+my $ssps = get_conf("ssps", 0);
2247+my $num_moreflds = get_conf("moreflds", 0);
2248+my $moreflds_prefix = get_conf("moreflds_prefix", "column0123456789_");
2249+my $keytype = get_conf("keytype", "varchar(32)");
2250+my $file = get_conf("file", undef);
2251+
2252+my $dsn = "DBI:mysql:database=;host=$host;port=$mysqlport"
2253+ . ";mysql_server_prepare=$ssps";
2254+my $dbh = DBI->connect($dsn, $mysqluser, $mysqlpass, { RaiseError => 1 });
2255+my $hsargs = { 'host' => $host, 'port' => $hsport };
2256+my $cli = new Net::HandlerSocket($hsargs);
2257+
2258+my @actions = split(/,/, $actions_str);
2259+for my $action (@actions) {
2260+ if ($action eq "table") {
2261+ print("TABLE $db.$table\n");
2262+ $dbh->do("drop database if exists $db");
2263+ $dbh->do("create database $db");
2264+ $dbh->do("use $db");
2265+ my $moreflds = get_createtbl_moreflds_str();
2266+ $dbh->do(
2267+ "create table $table (" .
2268+ "k $keytype primary key" .
2269+ ",v varchar(32) not null" .
2270+ $moreflds .
2271+ ") character set utf8 collate utf8_bin " .
2272+ "engine = $engine");
2273+ } elsif ($action eq "insert") {
2274+ print("INSERT $db.$table tablesize=$tablesize\n");
2275+ $dbh->do("use $db");
2276+ my $moreflds = get_insert_moreflds_str();
2277+ for (my $i = 0; $i < $tablesize; $i += 100) {
2278+ my $qstr = "insert into $db.$table values";
2279+ for (my $j = 0; $j < 100; ++$j) {
2280+ if ($j != 0) {
2281+ $qstr .= ",";
2282+ }
2283+ my $k = "" . ($i + $j);
2284+ my $v = "v" . int(rand(1000)) . ($i + $j);
2285+ $qstr .= "('$k', '$v'";
2286+ for (my $j = 0; $j < $num_moreflds; ++$j) {
2287+ $qstr .= ",'$j'";
2288+ }
2289+ $qstr .= ")";
2290+ }
2291+ $dbh->do($qstr);
2292+ print "$i/$tablesize\n" if $i % 1000 == 0;
2293+ }
2294+ } elsif ($action eq "read") {
2295+ print("READ $db.$table op=$op loop=$loop\n");
2296+ $dbh->do("use $db");
2297+ my $moreflds = get_select_moreflds_str();
2298+ my $sth = $dbh->prepare(
2299+ "select k,v$moreflds from $db.$table where k = ?");
2300+ for (my $i = 0; $i < $loop; ++$i) {
2301+ my $k = "" . int(rand($tablesize));
2302+ # print "k=$k\n";
2303+ $sth->execute($k);
2304+ if ($verbose >= 10) {
2305+ print "RET:";
2306+ while (my $ref = $sth->fetchrow_arrayref()) {
2307+ my $rk = $ref->[0];
2308+ my $rv = $ref->[1];
2309+ print " $rk $rv";
2310+ }
2311+ print "\n";
2312+ }
2313+ print "$i/$loop\n" if $i % 1000 == 0;
2314+ }
2315+ } elsif ($action eq "hsinsert") {
2316+ print("HSINSERT $db.$table tablesize=$tablesize\n");
2317+ $cli->open_index(1, $db, $table, '', 'k,v');
2318+ for (my $i = 0; $i < $tablesize; ++$i) {
2319+ my $k = "" . $i;
2320+ my $v = "v" . int(rand(1000)) . $i;
2321+ my $r = $cli->execute_insert(1, [ $k, $v ]);
2322+ if ($r->[0] != 0) {
2323+ die;
2324+ }
2325+ print "$i/$tablesize\n" if $i % 1000 == 0;
2326+ }
2327+ } elsif ($action eq "hsread") {
2328+ print("HSREAD $db.$table op=$op loop=$loop\n");
2329+ my $moreflds = get_select_moreflds_str();
2330+ $cli->open_index(1, $db, $table, '', "k,v$moreflds");
2331+ for (my $i = 0; $i < $loop; ++$i) {
2332+ my $k = "" . int(rand($tablesize));
2333+ # print "k=$k\n";
2334+ my $r = $cli->execute_find(1, $op, [ $k ], 1, 0);
2335+ if ($verbose >= 10) {
2336+ my $len = scalar(@{$r});
2337+ print "LEN=$len";
2338+ for my $e (@{$r}) {
2339+ print " [$e]";
2340+ }
2341+ print "\n";
2342+ }
2343+ print "$i/$loop\n" if $i % 1000 == 0;
2344+ }
2345+ } elsif ($action eq "hsupdate") {
2346+ my $vbase = "v" . int(rand(1000));
2347+ print("HSUPDATE $db.$table op=$op loop=$loop vbase=$vbase\n");
2348+ $cli->open_index(1, $db, $table, '', 'v');
2349+ for (my $i = 0; $i < $loop; ++$i) {
2350+ my $k = "" . int(rand($tablesize));
2351+ my $v = $vbase . $i;
2352+ print "k=$k v=$v\n";
2353+ my $r = $cli->execute_update(1, $op, [ $k ], 1, 0,
2354+ [ $v ]);
2355+ if ($verbose >= 10) {
2356+ print "UP k=$k v=$v\n";
2357+ }
2358+ print "$i/$loop\n" if $i % 1000 == 0;
2359+ }
2360+ } elsif ($action eq "hsdelete") {
2361+ print("HSDELETE $db.$table op=$op loop=$loop\n");
2362+ $cli->open_index(1, $db, $table, '', '');
2363+ for (my $i = 0; $i < $loop; ++$i) {
2364+ my $k = "" . int(rand($tablesize));
2365+ print "k=$k\n";
2366+ my $r = $cli->execute_delete(1, $op, [ $k ], 1, 0);
2367+ if ($verbose >= 10) {
2368+ print "DEL k=$k\n";
2369+ }
2370+ print "$i/$loop\n" if $i % 1000 == 0;
2371+ }
2372+ } elsif ($action eq "verify") {
2373+ verify_do();
2374+ }
2375+}
2376+
2377+sub verify_do {
2378+ my ($fail_cnt, $ok_cnt) = (0, 0);
2379+ my $sth = $dbh->prepare("select v from $db.$table where k = ?");
2380+ use FileHandle;
2381+ my $fh = new FileHandle($file, "r");
2382+ while (my $line = <$fh>) {
2383+ chomp($line);
2384+ my @vec = split(/\t/, $line);
2385+ my $k = $vec[3];
2386+ my $v = $vec[7];
2387+ next if (!defined($k) || !defined($v));
2388+ # print "$k $v\n";
2389+ $sth->execute($k);
2390+ my $aref = $sth->fetchrow_arrayref();
2391+ if (!defined($aref)) {
2392+ print "FAILED: $k notfound\n";
2393+ ++$fail_cnt;
2394+ } else {
2395+ my $gv = $aref->[0];
2396+ if ($gv ne $v) {
2397+ print "FAILED: $k got=$gv expected=$v\n";
2398+ ++$fail_cnt;
2399+ } else {
2400+ print "OK: $k $v $gv\n" if $verbose >= 10;
2401+ ++$ok_cnt;
2402+ }
2403+ }
2404+ }
2405+ print "OK=$ok_cnt FAIL=$fail_cnt\n";
2406+}
2407+
2408+sub get_conf {
2409+ my ($key, $def) = @_;
2410+ my $val = $conf{$key};
2411+ if ($val) {
2412+ print "$key=$val\n";
2413+ } else {
2414+ $val = $def;
2415+ $def ||= '';
2416+ print "$key=$def(default)\n";
2417+ }
2418+ return $val;
2419+}
2420+
2421+sub get_createtbl_moreflds_str {
2422+ my $s = "";
2423+ for (my $j = 0; $j < $num_moreflds; ++$j) {
2424+ $s .= ",$moreflds_prefix$j varchar(30)";
2425+ }
2426+ return $s;
2427+}
2428+
2429+sub get_select_moreflds_str {
2430+ my $s = "";
2431+ for (my $i = 0; $i < $num_moreflds; ++$i) {
2432+ $s .= ",$moreflds_prefix$i";
2433+ }
2434+ return $s;
2435+}
2436+
2437+sub get_insert_moreflds_str {
2438+ my $s = "";
2439+ for (my $i = 0; $i < $num_moreflds; ++$i) {
2440+ $s .= ",?";
2441+ }
2442+ return $s;
2443+}
2444+
2445
2446=== added file 'HandlerSocket-Plugin-for-MySQL/client/hstest_hs.sh'
2447--- HandlerSocket-Plugin-for-MySQL/client/hstest_hs.sh 1970-01-01 00:00:00 +0000
2448+++ HandlerSocket-Plugin-for-MySQL/client/hstest_hs.sh 2011-04-12 04:16:24 +0000
2449@@ -0,0 +1,4 @@
2450+#!/bin/bash
2451+
2452+exec ./hstest test=10 tablesize=10000 host=localhost hsport=9998 num=10000000 \
2453+ num_threads=100 timelimit=10 $@
2454
2455=== added file 'HandlerSocket-Plugin-for-MySQL/client/hstest_hs_more50.sh'
2456--- HandlerSocket-Plugin-for-MySQL/client/hstest_hs_more50.sh 1970-01-01 00:00:00 +0000
2457+++ HandlerSocket-Plugin-for-MySQL/client/hstest_hs_more50.sh 2011-04-12 04:16:24 +0000
2458@@ -0,0 +1,4 @@
2459+#!/bin/bash
2460+
2461+exec ./hstest test=10 key_mask=9999 host=localhost port=9998 num=10000000 \
2462+ num_threads=100 timelimit=10 moreflds=50 $@
2463
2464=== added file 'HandlerSocket-Plugin-for-MySQL/client/hstest_md.sh'
2465--- HandlerSocket-Plugin-for-MySQL/client/hstest_md.sh 1970-01-01 00:00:00 +0000
2466+++ HandlerSocket-Plugin-for-MySQL/client/hstest_md.sh 2011-04-12 04:16:24 +0000
2467@@ -0,0 +1,7 @@
2468+#!/bin/bash
2469+
2470+./hstest test=7 key_mask=9999 host=localhost port=11211 num=10000 \
2471+ num_threads=10 timelimit=10 op=R $@
2472+./hstest test=7 key_mask=9999 host=localhost port=11211 num=1000000 \
2473+ num_threads=100 timelimit=10 op=G $@
2474+
2475
2476=== added file 'HandlerSocket-Plugin-for-MySQL/client/hstest_my.sh'
2477--- HandlerSocket-Plugin-for-MySQL/client/hstest_my.sh 1970-01-01 00:00:00 +0000
2478+++ HandlerSocket-Plugin-for-MySQL/client/hstest_my.sh 2011-04-12 04:16:24 +0000
2479@@ -0,0 +1,3 @@
2480+#!/bin/bash
2481+exec ./hstest test=9 tablesize=9999 host=localhost mysqlport=3306 num=1000000 \
2482+ num_threads=100 verbose=1 timelimit=10 $@
2483
2484=== added file 'HandlerSocket-Plugin-for-MySQL/client/hstest_my_more50.sh'
2485--- HandlerSocket-Plugin-for-MySQL/client/hstest_my_more50.sh 1970-01-01 00:00:00 +0000
2486+++ HandlerSocket-Plugin-for-MySQL/client/hstest_my_more50.sh 2011-04-12 04:16:24 +0000
2487@@ -0,0 +1,3 @@
2488+#!/bin/bash
2489+exec ./hstest test=9 key_mask=9999 host=localhost port=3306 num=1000000 \
2490+ num_threads=100 verbose=1 timelimit=10 moreflds=50 $@
2491
2492=== added file 'HandlerSocket-Plugin-for-MySQL/configure.ac'
2493--- HandlerSocket-Plugin-for-MySQL/configure.ac 1970-01-01 00:00:00 +0000
2494+++ HandlerSocket-Plugin-for-MySQL/configure.ac 2011-04-12 04:16:24 +0000
2495@@ -0,0 +1,134 @@
2496+# -*- Autoconf -*-
2497+# Process this file with autoconf to produce a configure script.
2498+
2499+#AC_PREREQ([2.63b])
2500+AC_INIT([handlersocket-plugin], [1.0.6], [https://github.com/ahiguti/HandlerSocket-Plugin-for-MySQL/issues])
2501+AC_CONFIG_HEADERS([config.h])
2502+AM_INIT_AUTOMAKE([-Wall -Werror foreign])
2503+AC_CONFIG_SRCDIR([libhsclient/fatal.cpp])
2504+AC_CONFIG_MACRO_DIR([m4])
2505+
2506+AC_PROG_CC
2507+AC_PROG_CXX
2508+AC_PROG_CPP
2509+AC_PROG_LIBTOOL
2510+
2511+AC_DEFUN([CONFIG_OPTION_MYSQL],[
2512+ AC_MSG_CHECKING([mysql source])
2513+
2514+ MYSQL_SOURCE_VERSION=
2515+ MYSQL_INC=
2516+ ac_mysql_source_dir=
2517+ AC_ARG_WITH([mysql-source],
2518+ [AS_HELP_STRING([--with-mysql-source=PATH], [MySQL source directory PATH])],
2519+ [
2520+ ac_mysql_source_dir=`cd $withval && pwd`
2521+ if test -f "$ac_mysql_source_dir/sql/handler.h" ; then
2522+ MYSQL_INC="-I$ac_mysql_source_dir/sql"
2523+ MYSQL_INC="$MYSQL_INC -I$ac_mysql_source_dir/include"
2524+ MYSQL_INC="$MYSQL_INC -I$ac_mysql_source_dir/regex"
2525+ MYSQL_INC="$MYSQL_INC -I$ac_mysql_source_dir"
2526+ AC_SUBST(MYSQL_INC)
2527+ if test -f "$ac_mysql_source_dir/VERSION"; then
2528+ source "$ac_mysql_source_dir/VERSION"
2529+ MYSQL_SOURCE_VERSION="$MYSQL_VERSION_MAJOR.$MYSQL_VERSION_MINOR.$MYSQL_VERSION_PATCH"
2530+ else
2531+ if test -f "$ac_mysql_source_dir/configure.in"; then
2532+ 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|"`
2533+ else
2534+ AC_MSG_ERROR([invalid MySQL source directory: $ac_mysql_source_dir])
2535+ fi
2536+ fi
2537+ AC_MSG_RESULT([yes: Using $ac_mysql_source_dir, version $MYSQL_SOURCE_VERSION])
2538+ else
2539+ AC_MSG_ERROR([invalid MySQL source directory: $ac_mysql_source_dir])
2540+ fi
2541+ ],
2542+ [AC_MSG_ERROR([--with-mysql-source=PATH is required for standalone build])]
2543+ )
2544+
2545+ MYSQL_BIN_VERSION=
2546+ ac_mysql_config=
2547+ AC_ARG_WITH([mysql-bindir],
2548+ [AS_HELP_STRING([--with-mysql-bindir=PATH], [MySQL binary directory PATH. This should be the directory where mysql_config is located.])],
2549+ [
2550+ mysql_bin_dir=`cd $withval 2> /dev/null && pwd || echo ""`
2551+ ac_mysql_config="$mysql_bin_dir/mysql_config"
2552+ ],
2553+ [
2554+ AC_PATH_PROG([ac_mysql_config], [mysql_config])
2555+ ]
2556+ )
2557+
2558+ AC_MSG_CHECKING([mysql binary])
2559+ if test ! -x "$ac_mysql_config" ; then
2560+ AC_MSG_ERROR([mysql_config not found! You have to specify the directory where mysql_config resides to --with-mysql-bindir=PATH.])
2561+ fi
2562+
2563+ MYSQL_CFLAGS_ADD=`"$ac_mysql_config" --cflags`
2564+ MYSQL_CFLAGS="$MYSQL_CFLAGS $MYSQL_CFLAGS_ADD -DFORCE_DBUG_OFF"
2565+ # FIXME
2566+ AC_SUBST(MYSQL_CFLAGS)
2567+
2568+ MYSQL_BIN_VERSION=`"$ac_mysql_config" --version`
2569+ AC_MSG_RESULT([yes: Using $ac_mysql_config, version $MYSQL_BIN_VERSION])
2570+
2571+ MYSQL_LIB=`"$ac_mysql_config" --libs_r`
2572+ LIB_DIR=`echo $MYSQL_LIB | sed -e "s|.*-L/|/|" | sed -e "s| .*||"`
2573+ # FIXME
2574+ if test a`basename "$LIB_DIR"` = amysql ; then
2575+ MYSQL_LIB="-L`dirname $LIB_DIR` $MYSQL_LIB"
2576+ # FIXME
2577+ fi
2578+ AC_SUBST(MYSQL_LIB)
2579+
2580+ if test a$MYSQL_SOURCE_VERSION != a$MYSQL_BIN_VERSION ; then
2581+ AC_MSG_ERROR([MySQL source version does not match MySQL binary version])
2582+ fi
2583+
2584+ AC_MSG_CHECKING([mysql plugin dir])
2585+ ac_mysql_plugin_dir=
2586+ AC_ARG_WITH([mysql-plugindir],
2587+ [AS_HELP_STRING([--with-mysql-plugindir=PATH], [MySQL plugin directory where handlersocket.so to be copied])],
2588+ [
2589+ ac_mysql_plugin_dir=`cd $withval && pwd`
2590+ if test -d "$ac_mysql_plugin_dir/" ; then
2591+ PLUGIN_DIR="$ac_mysql_plugin_dir"
2592+ AC_SUBST(PLUGIN_DIR)
2593+ AC_MSG_RESULT([yes: Using $ac_mysql_plugin_dir])
2594+ else
2595+ AC_MSG_ERROR([invalid MySQL plugin directory : $ac_mysql_plugin_dir])
2596+ fi
2597+ ],
2598+ [
2599+ LIB_DIR_TMP=`"$ac_mysql_config" --plugindir`
2600+ if test ! -d "$LIB_DIR_TMP"; then
2601+ LIB_DIR_TMP=`"$ac_mysql_config" --libs_r | sed -e "s|.*-L/|/|" | sed -e "s| .*||"`/plugin
2602+ # FIXME
2603+ fi
2604+ ac_mysql_plugin_dir=$LIB_DIR_TMP
2605+ PLUGIN_DIR="$ac_mysql_plugin_dir"
2606+ AC_SUBST(PLUGIN_DIR)
2607+ AC_MSG_RESULT([--with-mysql-plugindir was not set. Using $ac_mysql_plugin_dir])
2608+ ]
2609+ )
2610+])
2611+
2612+HANDLERSOCKET_SUBDIRS="libhsclient"
2613+AC_ARG_ENABLE(handlersocket_server,
2614+ [ --enable-handlersocket-server build HandlerSocket plugin (defalut=yes)])
2615+if test "$enable_handlersocket_server" != "no"; then
2616+ CONFIG_OPTION_MYSQL
2617+ HANDLERSOCKET_SUBDIRS="libhsclient handlersocket client"
2618+fi
2619+AC_SUBST(HANDLERSOCKET_SUBDIRS)
2620+
2621+CFLAGS="$CFLAGS -Werror"
2622+CXXFLAGS="$CXXFLAGS -Wall -g -fno-rtti -fno-exceptions -fPIC -DPIC"
2623+
2624+AC_CONFIG_FILES([Makefile
2625+ handlersocket/Makefile
2626+ libhsclient/Makefile
2627+ client/Makefile])
2628+
2629+AC_OUTPUT
2630
2631=== added directory 'HandlerSocket-Plugin-for-MySQL/docs-en'
2632=== added file 'HandlerSocket-Plugin-for-MySQL/docs-en/about-handlersocket.en.txt'
2633--- HandlerSocket-Plugin-for-MySQL/docs-en/about-handlersocket.en.txt 1970-01-01 00:00:00 +0000
2634+++ HandlerSocket-Plugin-for-MySQL/docs-en/about-handlersocket.en.txt 2011-04-12 04:16:24 +0000
2635@@ -0,0 +1,72 @@
2636+
2637+-----------------------------------------------------------------------------
2638+HandlerSocket plugin for MySQL
2639+
2640+Copyright (c) 2010 DeNA Co.,Ltd.
2641+All rights reserved.
2642+
2643+Redistribution and use in source and binary forms, with or without
2644+modification, are permitted provided that the following conditions are met:
2645+
2646+ * Redistributions of source code must retain the above copyright
2647+ notice, this list of conditions and the following disclaimer.
2648+ * Redistributions in binary form must reproduce the above copyright
2649+ notice, this list of conditions and the following disclaimer in the
2650+ documentation and/or other materials provided with the distribution.
2651+ * Neither the name of DeNA Co.,Ltd. nor the names of its contributors
2652+ may be used to endorse or promote products derived from this software
2653+ without specific prior written permission.
2654+
2655+THIS SOFTWARE IS PROVIDED BY DeNA Co.,Ltd. "AS IS" AND ANY EXPRESS OR
2656+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
2657+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
2658+EVENT SHALL DeNA Co.,Ltd. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
2659+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
2660+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
2661+OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
2662+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
2663+OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
2664+ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2665+
2666+
2667+-----------------------------------------------------------------------------
2668+About HandlerSocket
2669+
2670+HandlerSocket is a NoSQL plugin for MySQL. It works as a daemon inside the
2671+mysqld process, accept tcp connections, and execute requests from clients.
2672+HandlerSocket does not support SQL queries. Instead, it supports simple CRUD
2673+operations on tables.
2674+
2675+Because of the following reasons, HandlerSocket is much faster than the
2676+mysqld/libmysql pair in some circumstances:
2677+
2678+ - HandlerSocket manipulates data without parsing SQL, which causes less
2679+ CPU usage.
2680+ - HandlerSocket reads many requests from clients and executes their
2681+ requests in bulk, which causes less CPU and disk usage.
2682+ - HandlerSocket client/server protocol is more compact than the
2683+ mysql/libmysql pair, which causes less network usage.
2684+
2685+The current version of HandlerSocket only works with GNU/Linux. The source
2686+archive of HandlerSocket includes a C++ and a Perl client libraries.
2687+Here is a list of other language bindings:
2688+
2689+ - PHP
2690+ http://openpear.org/package/Net_HandlerSocket
2691+ http://github.com/tz-lom/HSPHP
2692+ http://code.google.com/p/php-handlersocket/
2693+ - Java
2694+ http://code.google.com/p/handlersocketforjava/
2695+ - Python
2696+ https://code.launchpad.net/~songofacandy/+junk/pyhandlersocket
2697+ - Ruby
2698+ https://github.com/winebarrel/ruby-handlersocket
2699+ https://github.com/miyucy/handlersocket
2700+ - JavaScript(Node.js)
2701+ https://github.com/koichik/node-handlersocket
2702+
2703+The home of HandlerSocket is here:
2704+ https://github.com/ahiguti/HandlerSocket-Plugin-for-MySQL
2705+
2706+More documents are available in docs-en/ and docs-ja/ directories.
2707+
2708
2709=== added file 'HandlerSocket-Plugin-for-MySQL/docs-en/configuration-options.en.txt'
2710--- HandlerSocket-Plugin-for-MySQL/docs-en/configuration-options.en.txt 1970-01-01 00:00:00 +0000
2711+++ HandlerSocket-Plugin-for-MySQL/docs-en/configuration-options.en.txt 2011-04-12 04:16:24 +0000
2712@@ -0,0 +1,87 @@
2713+
2714+-----------------------------------------------------------------
2715+handlersocket_verbose (default = 10, min = 0, max = 10000)
2716+
2717+ Specify the logging verboseness.
2718+
2719+-----------------------------------------------------------------
2720+handlersocket_address (default = '')
2721+
2722+ Specify the address to bind. If empty, it binds to 0.0.0.0.
2723+
2724+-----------------------------------------------------------------
2725+handlersocket_port (default = '9998')
2726+
2727+ Specify the port to bind. This option is for the listener for
2728+ read requests. If empty, the listener is disabled.
2729+
2730+-----------------------------------------------------------------
2731+handlersocket_port_wr (default = '9999')
2732+
2733+ Specify the port to bind. This option is for the listener for
2734+ write requests. If empty, the listener is disabled.
2735+
2736+-----------------------------------------------------------------
2737+handlersocket_epoll (default = 1, min = 0, max = 1)
2738+
2739+ Specify whether handlersocket uses epoll for I/O multiplexing.
2740+
2741+-----------------------------------------------------------------
2742+handlersocket_threads (default = 16, min = 1, max = 3000)
2743+
2744+ Specify the number of handlersocket worker threads. This option
2745+ is for the listener for read requests. Recommended value is
2746+ (the number of CPU cores * 2).
2747+
2748+-----------------------------------------------------------------
2749+handlersocket_threads_wr (default = 1, min = 1, max = 3000)
2750+
2751+ Specify the number of handlersocket worker threads. This option
2752+ is for the listener for write requests. Recommended value is 1.
2753+
2754+-----------------------------------------------------------------
2755+handlersocket_timeout (default = 300, min = 30, max = 3600)
2756+
2757+ Specify the socket timeout in seconds.
2758+
2759+-----------------------------------------------------------------
2760+handlersocket_backlog (default = 32768, min = 5, max = 1000000)
2761+
2762+ Specify the length of the listen backlog.
2763+
2764+-----------------------------------------------------------------
2765+handlersocket_sndbuf (default = 0, min = 0, max = 1677216)
2766+
2767+ Specify the maximum socket send buffer in bytes. If 0, the
2768+ system-wide default value is set.
2769+
2770+-----------------------------------------------------------------
2771+handlersocket_rcvbuf (default = 0, min = 0, max = 1677216)
2772+
2773+ Specify the maximum socket receive buffer in bytes. If 0, the
2774+ system-wide default value is set.
2775+
2776+-----------------------------------------------------------------
2777+handlersocket_readsize (default = 0, min = 0, max = 1677216)
2778+
2779+ Specify the minimum length of the handlersocket request buffer.
2780+ Larger value can make handlersocket faster for large requests,
2781+ but can consume memory. The default value is possibly 4096.
2782+
2783+-----------------------------------------------------------------
2784+handlersocket_accept_balance (default = 0, min = 0, max = 10000)
2785+
2786+ When this option is set to non-zero, handlersocket tries to
2787+ balance accepted connections among threads. Non-zero is
2788+ recommended if you use persistent connections (i.e., connection
2789+ pooling on the client side).
2790+
2791+-----------------------------------------------------------------
2792+handlersocket_wrlock_timeout (default = 12, min = 0, max = 3600)
2793+
2794+ Specify the lock timeout in seconds. When a write request is
2795+ performed, handlersocket locks an advisory lock named
2796+ 'handlersocket_wr'. This option sets the timeout for the
2797+ locking.
2798+
2799+
2800
2801=== added file 'HandlerSocket-Plugin-for-MySQL/docs-en/installation.en.txt'
2802--- HandlerSocket-Plugin-for-MySQL/docs-en/installation.en.txt 1970-01-01 00:00:00 +0000
2803+++ HandlerSocket-Plugin-for-MySQL/docs-en/installation.en.txt 2011-04-12 04:16:24 +0000
2804@@ -0,0 +1,91 @@
2805+1. Building Handlersocket
2806+
2807+ 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.
2808+ 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.
2809+ Since Handlersocket uses daemon plugin interface supported from MySQL 5.1,
2810+MySQL 5.1 or higher version is required.
2811+ Please make sure that you use identical MySQL version between MySQL source
2812+and MySQL binary. Otherwise you might encounter serious problems (i.e. server
2813+crash, etc).
2814+ Here are steps to build Handlersocket.
2815+
2816+* Get MySQL source code
2817+
2818+* Get MySQL binary
2819+
2820+* Build Handlersocket
2821+ $ ./autogen.sh
2822+ $ ./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
2823+
2824+ --with-mysql-source refers to the top of MySQL source directory,
2825+--with-mysql-bindir refers to where MySQL binary executables (i.e.
2826+mysql_config) are located, and --with-mysql-plugindir refers to a plugin
2827+directory where plugin libraries (*.so) are installed.
2828+
2829+ $ make
2830+ $ sudo make install
2831+
2832+ Both libhsclient and the handlersocket plugin will be installed.
2833+
2834+
2835+2. Using Handlersocket
2836+
2837+Append configuration options for handlersocket to my.cnf.
2838+
2839+ [mysqld]
2840+ loose_handlersocket_port = 9998
2841+ # the port number to bind to (for read requests)
2842+ loose_handlersocket_port_wr = 9999
2843+ # the port number to bind to (for write requests)
2844+ loose_handlersocket_threads = 16
2845+ # the number of worker threads (for read requests)
2846+ loose_handlersocket_threads_wr = 1
2847+ # the number of worker threads (for write requests)
2848+ open_files_limit = 65535
2849+ # to allow handlersocket accept many concurrent
2850+ # connections, make open_files_limit as large as
2851+ # possible.
2852+
2853+Log in to mysql as root, and execute the following query.
2854+
2855+ mysql> install plugin handlersocket soname 'handlersocket.so';
2856+
2857+If handlersocket.so is successfully installed, it starts
2858+accepting connections on port 9998 and 9999. Running
2859+'show processlist' should show handlersocket worker threads.
2860+
2861+-----------------------------------------------------------------
2862+On the client side, you need to install libhsclient for c++ apps
2863+and perl-Net-HandlerSocket for perl apps. They do not require
2864+MySQL to compile.
2865+
2866+ $ ./autogen.sh
2867+ $ ./configure --disable-handlersocket-server
2868+ $ make
2869+ $ sudo make install
2870+ $ cd perl-Net-HandlerSocket
2871+ $ perl Makefile.PL
2872+ $ make
2873+ $ sudo make install
2874+
2875+-----------------------------------------------------------------
2876+Alternatively, you can use the rpm installation. If your OS
2877+supports rpms, you can use the following commands to build and
2878+install handlersocket rpm packages.
2879+
2880+(Server side, installs HandlerSocket plugin)
2881+ $ ./autogen.sh
2882+ $ ./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
2883+ $ make rpm_cli
2884+ $ sudo rpm -U dist/RPMS/*/libhsclient*.rpm
2885+ $ make rpm_c
2886+ $ sudo rpm -U dist/RPMS/*/handlersocket*.rpm
2887+
2888+(Client side, installs client libraries)
2889+ $ ./autogen.sh
2890+ $ ./configure --disable-handlersocket-server
2891+ $ make rpm_cli
2892+ $ sudo rpm -U dist/RPMS/*/libhsclient*.rpm
2893+ $ make rpm_perl
2894+ $ sudo rpm -U dist/RPMS/*/perl-Net-HandlerSocket*.rpm
2895+
2896
2897=== added file 'HandlerSocket-Plugin-for-MySQL/docs-en/perl-client.en.txt'
2898--- HandlerSocket-Plugin-for-MySQL/docs-en/perl-client.en.txt 1970-01-01 00:00:00 +0000
2899+++ HandlerSocket-Plugin-for-MySQL/docs-en/perl-client.en.txt 2011-04-12 04:16:24 +0000
2900@@ -0,0 +1,126 @@
2901+
2902+-----------------------------------------------------------------
2903+To open a connection to the handlersocket plugin, you need to
2904+create a Net::HandlerSocket object.
2905+
2906+ use Net::HandlerSocket;
2907+ my $args = { host => 'localhost', port => 9998 };
2908+ my $hs = new Net::HandlerSocket($args);
2909+
2910+-----------------------------------------------------------------
2911+Before executing table operations, you need to open an index to
2912+work with.
2913+
2914+ my $err = $hs->open_index(3, 'database1', 'table1', 'PRIMARY',
2915+ 'f1,f2');
2916+ die $hs->get_error() if $res->[0] != 0;
2917+
2918+The first argument for open_index is an integer value which is
2919+used to identify an open table, which is only valid within the
2920+same Net::HandlerSocket object. The 4th argument is the name of
2921+index to open. If 'PRIMARY' is specified, the primary index is
2922+open. The 5th argument is a comma-separated list of column names.
2923+
2924+-----------------------------------------------------------------
2925+To read a record from a table using an index, call the
2926+execute_single method.
2927+
2928+ my $res = $hs->execute_single(3, '=', [ 'foo' ], 1, 0);
2929+ die $hs->get_error() if $res->[0] != 0;
2930+ shift(@$res);
2931+
2932+The first argument must be an integer which has specified as the
2933+first argument for open_index on the same Net::HandlerSocket
2934+object. The second argument specifies the search operation. The
2935+current version of handlersocket supports '=', '>=', '<=', '>',
2936+and '<'. The 3rd argument specifies the key to find, which must
2937+an arrayref whose length is equal to or smaller than the number
2938+of key columns of the index. The 4th and the 5th arguments
2939+specify the maximum number of records to be retrieved, and the
2940+number of records skipped before retrieving records. The columns
2941+to be retrieved are specified by the 5th argument for the
2942+corresponding open_index call.
2943+
2944+The execute_single method always returns an arrayref. The first
2945+element is the error code, which is 0 when no error is occured.
2946+The remaining are the field values. If more than one record is
2947+returned, it is flatten to an 1-dimensional array. For example,
2948+when 5 records that have 3 columns are returned, you can retrieve
2949+values using the following code.
2950+
2951+ die $hs->get_error() if $res->[0] != 0;
2952+ shift(@$res);
2953+ for (my $row = 0; $row < 5; ++$row) {
2954+ for (my $col = 0; $col < 3; ++$col) {
2955+ my $value = $res->[$row * 5 + $col];
2956+ # ...
2957+ }
2958+ }
2959+
2960+-----------------------------------------------------------------
2961+To update or delete records, you need to specify more arguments
2962+for the execute_single method. Note that the Net::HandlerSocket
2963+object must be connected to a handlersocket worker for write
2964+operations, which is port 9999 by default.
2965+(For safety, the port 9998 only allows read operations, and the
2966+port 9999 allows write operations also. The port 9999 allows
2967+read operations too, but slower than 9998 because of record
2968+locking etc.. Port numbers can be changed using the
2969+'handlersocket_port' and the 'handlersocket_port_wr'
2970+configuration options of mysqld.)
2971+
2972+ my $args = { host => 'localhost', port => 9999 };
2973+ my $hs = new Net::HandlerSocket($args);
2974+
2975+ my $res = $hs->execute_single(3, '=', [ 'bar' ], 1, 0, 'U',
2976+ [ 'fubar', 'hoge' ]);
2977+ die $hs->get_error() if $res->[0] != 0;
2978+ my $num_updated_rows = $res->[1];
2979+
2980+ my $res = $hs->execute_single(3, '=', [ 'baz' ], 1, 0, 'D');
2981+ die $hs->get_error() if $res->[0] != 0;
2982+ my $num_deleted_rows = $res->[1];
2983+
2984+The 6th argument for execute_single specifies the modification
2985+operation. The current version supports 'U' and 'D'. For the 'U'
2986+operation, the 7th argument specifies the new value for the row.
2987+The columns to be modified are specified by the 5th argument for
2988+the corresponding open_index call. For the 'D' operation, the
2989+7th argument can be omitted.
2990+
2991+-----------------------------------------------------------------
2992+The execute_single method can be used for inserting records also.
2993+
2994+ my $res = $hs->execute_single(3, '+', [ 'foo', 'bar', 'baz' ]);
2995+ die $hs->get_error() if $res->[0] != 0;
2996+ my $num_inserted_rows = $res->[1];
2997+
2998+The 3rd argument must be an arrayref whose elements correspond to
2999+the 5th argument for the corresponding open_index call. If there
3000+is a column which is not appeared in the 5th argument for the
3001+open_index, the default value for the column is set.
3002+
3003+-----------------------------------------------------------------
3004+Multiple operations can be executed in a single call. Executing
3005+multiple operations in a single call is much faster than
3006+executing them separatedly.
3007+
3008+ my $rarr = $hs->execute_multi([
3009+ [ 0, '>=', [ 'foo' ], 5, 0 ],
3010+ [ 2, '=', [ 'bar' ], 1, 0 ],
3011+ [ 4, '<', [ 'baz' ], 10, 5 ],
3012+ ]);
3013+ for my $res (@$rarr) {
3014+ die $hs->get_error() if $res->[0] != 0;
3015+ shift(@$res);
3016+ # ...
3017+ }
3018+
3019+-----------------------------------------------------------------
3020+When an error is occured, the first element of the returned
3021+arrayref becomes a non-zero value. A negative value indicates
3022+that an I/O error is occured and the Net::HandlerSocket object
3023+should be disposed. A positive value means that the connection is
3024+still active and the Net::HandlerSocket object can be reused
3025+later.
3026+
3027
3028=== added file 'HandlerSocket-Plugin-for-MySQL/docs-en/protocol.en.txt'
3029--- HandlerSocket-Plugin-for-MySQL/docs-en/protocol.en.txt 1970-01-01 00:00:00 +0000
3030+++ HandlerSocket-Plugin-for-MySQL/docs-en/protocol.en.txt 2011-04-12 04:16:24 +0000
3031@@ -0,0 +1,148 @@
3032+
3033+----------------------------------------------------------------------------
3034+The HandlerSocket protocol
3035+
3036+----------------------------------------------------------------------------
3037+Basic syntax
3038+
3039+- The HandlerSocket protocol is line-based. Each line ends with LF(0x0a).
3040+- Each line consists a concatenation of tokens separated by HT(0x09).
3041+- A token is either NULL or an encoded string. Note that you need to
3042+ distinguish NULL from an empty string, as most DBMs does so.
3043+- NULL is expressed as a single NUL(0x00).
3044+- An encoded string is a string with the following encoding rules.
3045+ - Characters in the range [0x10 - 0xff] are not encoded.
3046+ - A character in the range [0x00 - 0x0f] is prefixed by 0x01 and
3047+ shifted by 0x40. For example, 0x03 is encoded as 0x01 0x43.
3048+- Note that a string can be empty. A continuation of 0x09 0x09 means that
3049+ there is an empty string between them. A continuation of 0x09 0x0a means
3050+ that there is an empty string at the end of the line.
3051+
3052+----------------------------------------------------------------------------
3053+Request and Response
3054+
3055+- The HandlerSocket protocol is a simple request/response protocol. After a
3056+ connection is established, the client side sends a request, and then the
3057+ server side sends a response.
3058+- A request/response consists of a single line.
3059+- Requests can be pipelined; That is, you can send multiple requests (ie.
3060+ lines) at one time, and receive responses for them at one time.
3061+
3062+----------------------------------------------------------------------------
3063+'open_index' request
3064+
3065+The 'open_index' request has the following syntax.
3066+
3067+ P <indexid> <dbname> <tablename> <indexname> <columns>
3068+
3069+- <indexid> is a number in decimal.
3070+- <dbname>, <tablename>, and <indexname> are strings. To open the primary
3071+ key, use PRIMARY as <indexname>.
3072+- <columns> is a comma-separated list of column names.
3073+
3074+Once an 'open_index' request is issued, the HandlerSocket plugin opens the
3075+specified index and keep it open until the client connection is closed. Each
3076+open index is identified by <indexid>. If <indexid> is already open, the old
3077+open index is closed. You can open the same combination of <dbname>
3078+<tablename> <indexname> multple times, possibly with different <columns>.
3079+For efficiency, keep <indexid> small as far as possible.
3080+
3081+----------------------------------------------------------------------------
3082+Getting data
3083+
3084+The 'find' request has the following syntax.
3085+
3086+ <indexid> <op> <vlen> <v1> ... <vn> <limit> <offset>
3087+
3088+- <indexid> is a number. This number must be an <indexid> specified by a
3089+ 'open_index' request executed previously on the same connection.
3090+- <op> specifies the comparison operation to use. The current version of
3091+ HandlerSocket supports '=', '>', '>=', '<', and '<='.
3092+- <vlen> indicates the length of the trailing parameters <v1> ... <vn>. This
3093+ must be smaller than or equal to the number of index columns specified by
3094+ specified by the corresponding 'open_index' request.
3095+- <v1> ... <vn> specify the index column values to fetch.
3096+- <limit> and <offset> are numbers. These parameters can be omitted. When
3097+ omitted, it works as if 1 and 0 are specified.
3098+
3099+----------------------------------------------------------------------------
3100+Updating/Deleting data
3101+
3102+The 'find_modify' request has the following syntax.
3103+
3104+ <indexid> <op> <vlen> <v1> ... <vn> <limit> <offset> <mop> <m1> ... <mk>
3105+
3106+- <mop> is either 'U' (update) or 'D' (delete).
3107+- <m1> ... <mk> specifies the column values to set. The length of <m1> ...
3108+ <mk> must be smaller than or equal to the length of <columns> specified by
3109+ the corresponding 'open_index' request. If <mop> is 'D', these parameters
3110+ are ignored.
3111+
3112+----------------------------------------------------------------------------
3113+Inserting data
3114+
3115+The 'insert' request has the following syntax.
3116+
3117+ <indexid> '+' <vlen> <v1> ... <vn>
3118+
3119+- <vlen> indicates the length of the trailing parameters <v1> ... <vn>. This
3120+ must be smaller than or equal to the length of <columns> specified by the
3121+ corresponding 'open_index' request.
3122+- <v1> ... <vn> specify the column values to set. For columns not in
3123+ <columns>, the default values for each column are set.
3124+
3125+----------------------------------------------------------------------------
3126+Response syntax
3127+
3128+HandlerSocket returns a response of the following syntax for each request.
3129+
3130+ <errorcode> <numcolumns> <r1> ... <rn>
3131+
3132+- <errorcode> indicates whether the request has successfully executed or not.
3133+ '0' means success. Non-zero means an error.
3134+- <numcolumns> indicates the number of columns of the result set.
3135+- <r1> ... <rn> is the result set. The length of <r1> ... <rn> is always a
3136+ multiple of <numcolumns>. It is possible that <r1> ... <rn> is empty.
3137+
3138+If <errorcode> is non-zero, <numcolumns> is always 1 and <r1> indicates a
3139+human-readable error message, though sometimes <r1> is not provided.
3140+
3141+----------------------------------------------------------------------------
3142+Response for 'open_index'
3143+
3144+If 'open_index' is succeeded, HandlerSocket returns a line of the following
3145+syntax.
3146+
3147+ 0 1
3148+
3149+----------------------------------------------------------------------------
3150+Response for 'find'
3151+
3152+If 'find' is succeeded, HandlerSocket returns a line of the following
3153+syntax.
3154+
3155+ 0 <numcolumns> <r1> ... <rn>
3156+
3157+- <numcolumns> always equals to the length of <columns> of the corresponding
3158+ 'open_index' request.
3159+- <r1> ... <rn> is the result set. If N rows are found, the length of <r1>
3160+ ... <rn> becomes ( <numcolumns> * N ).
3161+
3162+----------------------------------------------------------------------------
3163+Response for 'find_modify'
3164+
3165+If 'find_modify' is succeeded, HandlerSocket returns a line of the following
3166+syntax.
3167+
3168+ 0 1 <nummod>
3169+
3170+- <nummod> is the number of modified rows.
3171+
3172+----------------------------------------------------------------------------
3173+Response for 'insert'
3174+
3175+If 'insert' is succeeded, HanderSocket returns a line of the following
3176+syntax.
3177+
3178+ 0 1
3179+
3180
3181=== added directory 'HandlerSocket-Plugin-for-MySQL/docs-ja'
3182=== added file 'HandlerSocket-Plugin-for-MySQL/docs-ja/about-handlersocket.ja.txt'
3183--- HandlerSocket-Plugin-for-MySQL/docs-ja/about-handlersocket.ja.txt 1970-01-01 00:00:00 +0000
3184+++ HandlerSocket-Plugin-for-MySQL/docs-ja/about-handlersocket.ja.txt 2011-04-12 04:16:24 +0000
3185@@ -0,0 +1,51 @@
3186+
3187+
3188+-----------------------------------------------------------------
3189+ソースコード�利用�������責事項
3190+
3191+本ソフトウェア�開発者�よ�株�会社ディー・エヌ・エー��本フト
3192+ウェア��稼動�稼動�良を�む法律上�瑕疵担�責任���他�証責
3193+任を負���も�����。���本ソフトウエア�開発者�よ�株�
3194+会社ディー・エヌ・エー��本ソフトウェア�商�性�����客様�
3195+特定�目的�対�る��性��������る�証も負���も���
3196+��。
3197+
3198+-----------------------------------------------------------------
3199+handlersocket plugin����
3200+
3201+mysqlサー��常���innodb等�ストレージエンジン��直接�アクセ
3202+スを�供�るプラグイン��。handlersocketプラグイン�自��リス
3203+ナーを���専用�クライアントライブラリ(libhsclient)を使���れ
3204+�アクセス���。
3205+
3206+mysql�標準クライアントライブラリ(libmysql)を使��アクセス�比�
3207+��以下�よ��利点��り��。
3208+・接続��り�消費�るリソース�少������時接続数�事実上無
3209+ 制�。�����接続数を気����続接続を使���。
3210+・高速(�純��照クエリ�3�〜10�程度)。
3211+・通信プロトコル�コンパクト。libmysqlを使��データ転�時�レ
3212+ コード����付��る���通信内容�冗長����libhsclient�
3213+ �データ���転��れる���帯域消費�少���り��。場��
3214+ よ���10�以上libmysql����冗長��り��。
3215+
3216+�在��ージョン��以下�よ��処�をサ�ート�����。
3217+・指定�れ�索引�����指定�れ�値�完全一致�るよ��レコー
3218+ ドを�得。(SELECT ??? FROM tbl WHERE k1 = v1 AND k2 = v2...)。
3219+ 索引を使���検索�サ�ート�����ん。
3220+・指定�れ�索引�����指定�れ�値��置��後�レコードを�
3221+ 得。(SELECT ??? FROM tbl WHERE k1 >= v1 LIMIT 100)
3222+・�述�よ��手段��得��レコード�対�るUPDATE�DELETE
3223+・レコード�INSERT
3224+
3225+以下�よ��言語をサ�ート���。
3226+・C++。libhsclientをリンク���。
3227+・Perl。Net::HandlerSocketをuse���。
3228+
3229+�在��ージョン��GNU/Linux���動作���。
3230+
3231+-----------------------------------------------------------------
3232+既知��題
3233+
3234+・kill�handlersocketスレッドを殺���スレッド数�減����回復
3235+ ���ん。
3236+
3237
3238=== added file 'HandlerSocket-Plugin-for-MySQL/docs-ja/installation.ja.txt'
3239--- HandlerSocket-Plugin-for-MySQL/docs-ja/installation.ja.txt 1970-01-01 00:00:00 +0000
3240+++ HandlerSocket-Plugin-for-MySQL/docs-ja/installation.ja.txt 2011-04-12 04:16:24 +0000
3241@@ -0,0 +1,87 @@
3242+
3243+-----------------------------------------------------------------
3244+HandlerSocketプラグイン�ビルド方法(RPMを使���方法)
3245+
3246+以下�よ����configureを実行���。
3247+
3248+ $ ./autogen.sh
3249+ $ ./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
3250+
3251+���--with-mysql-source��MySQL�ソースコード�トップディレク
3252+トリを指定���。--with-mysql-bindir��インストール済��MySQL
3253+�mysql_configコマンド�有るディレクトリを指定���。
3254+��後以下�よ��ビルド・インストール���。
3255+
3256+ $ make
3257+ $ sudo make install
3258+
3259+-----------------------------------------------------------------
3260+クライアントライブラリ�ビルド方法(RPMを使���方法)
3261+
3262+クライアントライブラリをビルド�る際���MySQL�ソースコード�
3263+必��り��ん。��MySQL�インストール�れ��る必�も�り��ん。
3264+
3265+ $ ./autogen.sh
3266+ $ ./configure --disable-handlersocket-server
3267+ $ make
3268+ $ sudo make install
3269+ $ cd perl-Net-HandlerSocket
3270+ $ perl Makefile.PL
3271+ $ make
3272+ $ sudo make install
3273+
3274+-----------------------------------------------------------------
3275+ビルド方法(RPM)
3276+
3277+以下�よ��実行�れ��rpmパッケージ�ビルド&インストール�れ�
3278+�。
3279+
3280+(MySQLサー���HandlerSocketプラグインをインストール�る)
3281+ $ ./autogen.sh
3282+ $ ./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
3283+ $ make rpm_cli
3284+ $ sudo rpm -U dist/RPMS/*/libhsclient*.rpm
3285+ $ make rpm_c
3286+ $ sudo rpm -U dist/RPMS/*/handlersocket*.rpm
3287+
3288+(クライアント��クライアントライブラリをインストール�る)
3289+ $ ./autogen.sh
3290+ $ ./configure --disable-handlersocket-server
3291+ $ make rpm_cli
3292+ $ sudo rpm -U dist/RPMS/*/libhsclient*.rpm
3293+ $ make rpm_perl
3294+ $ sudo rpm -U dist/RPMS/*/perl-Net-HandlerSocket*.rpm
3295+
3296+-----------------------------------------------------------------
3297+èµ·å‹•
3298+
3299+mysqlを起動��状態��mysql�設定ファイル(my.cnf等)�以下�内容を
3300+追加���。
3301+
3302+ [mysqld]
3303+ handlersocket_port = 9998
3304+ # handlersocket�接続を��付�る�ート(�照系リクエスト用)
3305+ handlersocket_port_wr = 9999
3306+ # handlersocket�接続を��付�る�ート(更新系リクエスト用)
3307+ handlersocket_address =
3308+ # handlersocket��インド�るアドレス(空����OK)
3309+ handlersocket_verbose = 0
3310+ # デ�ッグ用
3311+ handlersocket_timeout = 300
3312+ # 通信タイムアウト(秒)
3313+ handlersocket_threads = 16
3314+ # handlersocket�ワーカースレッド数
3315+ thread_concurrency = 128
3316+ # handlersocket�幾���スレッドを�有�る���大���
3317+ # 値を指定������
3318+ open_files_limit = 65535
3319+ # ソケットを大��開�るよ���る���大���値を指定�
3320+ # �����
3321+
3322+以下�クエリを実行���。
3323+
3324+ mysql> install plugin handlersocket soname 'handlersocket.so';
3325+ Query OK, 0 rows affected (0.06 sec)
3326+
3327+以上�handlersocket�クライアント�らアクセス��るよ���り��。
3328+
3329
3330=== added file 'HandlerSocket-Plugin-for-MySQL/docs-ja/perl-client.ja.txt'
3331--- HandlerSocket-Plugin-for-MySQL/docs-ja/perl-client.ja.txt 1970-01-01 00:00:00 +0000
3332+++ HandlerSocket-Plugin-for-MySQL/docs-ja/perl-client.ja.txt 2011-04-12 04:16:24 +0000
3333@@ -0,0 +1,118 @@
3334+
3335+-----------------------------------------------------------------
3336+handlersocketプラグイン��接続を開����Net::HandlerSocketオブ
3337+ジェクトを作����。
3338+
3339+ use Net::HandlerSocket;
3340+ my $args = { host => 'localhost', port => 9998 };
3341+ my $hs = new Net::HandlerSocket($args);
3342+
3343+-----------------------------------------------------------------
3344+検索���命令を実行�る���処�対象��る索引を開�必���り
3345+��。
3346+
3347+ my $err = $hs->open_index(3, 'database1', 'table1', 'PRIMARY',
3348+ 'f1,f2');
3349+ die $hs->get_error() if $res->[0] != 0;
3350+
3351+最��引数�開�索引�付�る番���。付��番���一�
3352+Net::HandlerSocketオブジェクト������有効��。第4引数�開�
3353+索引�����「PRIMARY��指定�れ場��プライマリキー�開�れ�
3354+�。第5引数�「,��区切られ�列��リスト��。
3355+
3356+-----------------------------------------------------------------
3357+テーブル�ら索引を使��行を�得�る���execute_singleメソッド
3358+を呼���。
3359+
3360+ my $res = $hs->execute_single(3, '=', [ 'foo' ], 1, 0);
3361+ die $hs->get_error() if $res->[0] != 0;
3362+ shift(@$res);
3363+
3364+最��引数�索引�番�����Net::HandlerSocketオブジェクト�
3365+open_index�付��も����れ��り��ん。第2引数���作を指定
3366+���。�在��ージョン���「=��「>=��「<=��「>��「<�
3367+��作�利用�能��。第3引数��列���照���れ�探���行�
3368+キー値を指定���。�列�長��索引�キー�個数����少��数
3369+���れ��り��ん。第4引数�第5引数��れ�れ��得�る最大行
3370+数��得��読�飛��行数を指定���。�得�れる列�対応�る
3371+open_index呼�出��第5引数�指定�れ�も���り��。
3372+
3373+execute_singleメソッド�常��列���照を返���。最���素�
3374+エラーコード���れ�0�ら��功を表���。残り��素�列�値�
3375+�。も��得�れ�データ�複数行�場����れ�一���列�連�
3376+�れ�形�格��れ����。例���5行3列�データ�場��次�よ
3377+��コード�よ����内容を�得����。
3378+
3379+ die $hs->get_error() if $res->[0] != 0;
3380+ shift(@$res);
3381+ for (my $row = 0; $row < 5; ++$row) {
3382+ for (my $col = 0; $col < 3; ++$col) {
3383+ my $value = $res->[$row * 5 + $col];
3384+ # ...
3385+ }
3386+ }
3387+
3388+-----------------------------------------------------------------
3389+行を更新���削除�る���更�多��引数を指定��
3390+execute_singleメソッドを呼�出���。書�込�処�を�る���
3391+対象�Net::HandlerSocketオブジェクト�更新用handlersocketワーカ(既
3392+定���ート9999)�接続�れ�も�������り��ん。
3393+(安全�����ート9998��照処���を��付���ート9999�更新
3394+処�も��付�るよ��������。�ート9999��照処�も��付
3395+�����レコードロック等�影響����り��。�ート番��
3396+mysqld�「handlersocket_port��「handlersocket_port_wr��設定項
3397+目�変更����。)
3398+
3399+ my $args = { host => 'localhost', port => 9999 };
3400+ my $hs = new Net::HandlerSocket($args);
3401+
3402+ my $res = $hs->execute_single(3, '=', [ 'bar' ], 1, 0, 'U',
3403+ [ 'fubar', 'hoge' ]);
3404+ die $hs->get_error() if $res->[0] != 0;
3405+ my $num_updated_rows = $res->[1];
3406+
3407+ my $res = $hs->execute_single(3, '=', [ 'baz' ], 1, 0, 'D');
3408+ die $hs->get_error() if $res->[0] != 0;
3409+ my $num_deleted_rows = $res->[1];
3410+
3411+execute_single�第6引数�変更処��種類を指定���。�在��ー
3412+ジョン��「U��「D��利用�能��。「U�������第7引数�
3413+新��値を指定���。更新�れる列��対応�るopen_index呼�出�
3414+�第5引数�指定�れ�も���り��。「D������第7引数��
3415+略����。
3416+
3417+-----------------------------------------------------------------
3418+execute_singleメソッド�列�挿入�も使用����。
3419+
3420+ my $res = $hs->execute_single(3, '+', [ 'foo', 'bar', 'baz' ]);
3421+ die $hs->get_error() if $res->[0] != 0;
3422+ my $num_inserted_rows = $res->[1];
3423+
3424+第3引数��対応�るopen_index呼�出��第5引数�列リスト����
3425+��長���列���照���れ��り��ん。open_index呼�出��
3426+第5引数�指定�れ����列��������列�既定値�セット�れ
3427+��。
3428+
3429+-----------------------------------------------------------------
3430+execute_multiメソッドを使���複数�リクエストを一��呼�出��
3431+実行�る�������。�れ�リクエストを個別�実行�るより高速
3432+��。
3433+
3434+ my $rarr = $hs->execute_multi([
3435+ [ 0, '>=', [ 'foo' ], 5, 0 ],
3436+ [ 2, '=', [ 'bar' ], 1, 0 ],
3437+ [ 4, '<', [ 'baz' ], 10, 5 ],
3438+ ]);
3439+ for my $res (@$rarr) {
3440+ die $hs->get_error() if $res->[0] != 0;
3441+ shift(@$res);
3442+ # ...
3443+ }
3444+
3445+-----------------------------------------------------------------
3446+エラー�起�る�返値��列�照�最���素�0以外��り��。負�
3447+数�場��I/Oエラー�起�����を示����場����
3448+Net::HandlerSocketオブジェクト�破棄�る����。正�値�場��
3449+接続�維��れ��る�����オブジェクト��れ以後も�利用��
3450+��。
3451+
3452
3453=== added file 'HandlerSocket-Plugin-for-MySQL/docs-ja/protocol.ja.txt'
3454--- HandlerSocket-Plugin-for-MySQL/docs-ja/protocol.ja.txt 1970-01-01 00:00:00 +0000
3455+++ HandlerSocket-Plugin-for-MySQL/docs-ja/protocol.ja.txt 2011-04-12 04:16:24 +0000
3456@@ -0,0 +1,94 @@
3457+
3458+-----------------------------------------------------------------
3459+handlersocket�通信プロトコル
3460+
3461+-----------------------------------------------------------------
3462+構文
3463+
3464+・コマンド行�改行(LF)�終�る。
3465+・コマンド行�複数�トークン�ら�り�トークン間�TAB�区切られる。
3466+・トークン�NULLトークン��文字列トークン���れ�。
3467+・NULLトークン��一�NUL文字��ら��れる。
3468+・文字列トークン��0�イト以上�文字列��ら��れる。���0x10
3469+ 未満�文字�����0x01を�置��0x40を加��コード��ら��
3470+ れる。�れ以外�文字���文字自身�コード��ら��れる。
3471+
3472+-----------------------------------------------------------------
3473+リクエスト�レス�ンス
3474+
3475+・接続�確立��直後�状態�����クライアント�コマンド行を�
3476+ る。(リクエスト)
3477+・サー��クライアント����リクエスト��度��数�コマンド行
3478+ を返�。(レス�ンス)
3479+・リクエスト�パイプライン化��よ�。��りクライアント���
3480+ ���リクエスト�対�る返事を待���次�リクエストを���も
3481+ よ�。
3482+
3483+-----------------------------------------------------------------
3484+リクエスト
3485+
3486+・open_index命令�次�よ��構文を��。
3487+ 'P' indexid dbname tablename indexname fieldlist
3488+ indexid�開���る索引�付�られる番����一接続上�後�実行
3489+ �る命令��対象索引を指定�る���使�れる。dbname�tablename�
3490+ indexname��れ�れ開���DB�テーブル�索引���。索引���
3491+ ���"PRIMARY"を指定�る�プライマリキー�開�れる。fieldlist
3492+ �カンマ区切り�列��リスト。
3493+・find命令�次�よ��構文を��。
3494+ indexid op nflds v1 ... vn limit offset
3495+ indexid�実行対象�索引を指定�る。op�索引検索�演算�(後述)。
3496+ v1�らvn��変長����個数�nflds。nflds�indexid�指定�れ�
3497+ open_index命令�indexname�索引�fieldlist�フィールド数�等�
3498+ ����������ら��。m2�らmk��変長����個数�
3499+ indexid�指定�れ�open_index命令�発行�れ�際�fieldlist�一
3500+ 致���れ��ら��。コマンド行�limit以���略��る。limit
3501+ �offset��検索�件��致�る列���レス�ンス�返�列数�上
3502+ ���スキップ�る列数。limit�offsetを�略��場���れ�れ1
3503+ �0�指定�れ������動作を�る。find命令�レス�ンス����
3504+ �件��致��列�リストを返�。op���指定��る演算��次�
3505+ ��り。
3506+ '=' - v1 ... vn�一致�るも�を�得
3507+ '>' - v1 ... vnより大��も�を昇順��得
3508+ '>=' - v1 ... vn�一致�る�大��も�を昇順��得
3509+ '<' - v1 ... vnより���も�を�順��得
3510+ '<=' - v1 ... vn�一致�る�等��も�を�順��得
3511+ nflds�1より大��(v1 ... vn�2個以上)���辞書�順��比較�
3512+ れる。
3513+・find_modify命令�次�よ��構文を��。
3514+ indexid op nflds v1 ... vn limit offset modop m1 ... mk
3515+ modopより��部分�find命令��等���れ�よ���作対象�行を
3516+ 指定�る。���作対象�行�対�modop�指定�れ�変更処�を実行
3517+ �る。m1 ... mk��変長���略��る。modop�次��れ�。
3518+ 'U' - indexid�指定�れ�open_index命令�fieldlist列
3519+ �内容を�m1 ... mk�値�更新�る。
3520+ 'D' - 対象�行を削除�る。m1 ... mk�値�無視�れる。
3521+・insert命令��よ��構文を��。
3522+ indexid '+' nflds v1 ... vn
3523+ indexid�指定�れ�テーブル��列を挿入�る。v1 ... vn��変長
3524+ ����個数�nflds。nflds�indexid�指定�れ�open_index命令�
3525+ indexname�索引�fieldlist�フィールド数�等���������
3526+ ��ら��。
3527+
3528+-----------------------------------------------------------------
3529+レス�ンス
3530+
3531+・open_index命令��功�����レス�ンス�次�構文を��。
3532+ '0' '1'
3533+・find命令��功�����レス�ンス�次�構文を��。
3534+ '0' nflds v1 ... vn
3535+ nflds��果セット�列�数を�ら��。v1 ... vn��変長����
3536+ 長��nflds�整数�。v1 ... vn�空���も�り��れ��件��
3537+ 致�るレコード�存在�������を�ら��。�果セット�複数
3538+ 行�������v1 ... vn�長��nflds�2�以上��り�最��
3539+ 行�ら順�v1 ... vn�セット�れる。
3540+・modify命令��功�����レス�ンス�次�構文を��。
3541+ '0' '1' nummod
3542+ nummod�変更�施�れ�行数。nummod�0����変更�れ�行�無
3543+ �����を�ら��。
3544+・insert命令��功�����レス�ンス�次�構文を��。
3545+ '0' '1'
3546+・命令�失敗�����レス�ンス�命令�関�ら�次�構文を��。
3547+ err '1' message
3548+ err�0以外�数値��エラーコードを�ら��。message�人間�読�
3549+ エラーメッセージ。���message�無���も�る。
3550+
3551
3552=== added directory 'HandlerSocket-Plugin-for-MySQL/handlersocket'
3553=== added file 'HandlerSocket-Plugin-for-MySQL/handlersocket/COPYRIGHT.txt'
3554--- HandlerSocket-Plugin-for-MySQL/handlersocket/COPYRIGHT.txt 1970-01-01 00:00:00 +0000
3555+++ HandlerSocket-Plugin-for-MySQL/handlersocket/COPYRIGHT.txt 2011-04-12 04:16:24 +0000
3556@@ -0,0 +1,27 @@
3557+
3558+ Copyright (c) 2010 DeNA Co.,Ltd.
3559+ All rights reserved.
3560+
3561+ Redistribution and use in source and binary forms, with or without
3562+ modification, are permitted provided that the following conditions are met:
3563+
3564+ * Redistributions of source code must retain the above copyright
3565+ notice, this list of conditions and the following disclaimer.
3566+ * Redistributions in binary form must reproduce the above copyright
3567+ notice, this list of conditions and the following disclaimer in the
3568+ documentation and/or other materials provided with the distribution.
3569+ * Neither the name of DeNA Co.,Ltd. nor the names of its contributors
3570+ may be used to endorse or promote products derived from this software
3571+ without specific prior written permission.
3572+
3573+ THIS SOFTWARE IS PROVIDED BY DeNA Co.,Ltd. "AS IS" AND ANY EXPRESS OR
3574+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
3575+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
3576+ EVENT SHALL DeNA Co.,Ltd. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
3577+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
3578+ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
3579+ OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
3580+ WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
3581+ OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
3582+ ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3583+
3584
3585=== added file 'HandlerSocket-Plugin-for-MySQL/handlersocket/Makefile.am'
3586--- HandlerSocket-Plugin-for-MySQL/handlersocket/Makefile.am 1970-01-01 00:00:00 +0000
3587+++ HandlerSocket-Plugin-for-MySQL/handlersocket/Makefile.am 2011-04-12 04:16:24 +0000
3588@@ -0,0 +1,10 @@
3589+pkgplugindir = $(PLUGIN_DIR)
3590+noinst_HEADERS = database.hpp hstcpsvr.hpp hstcpsvr_worker.hpp mysql_incl.hpp
3591+pkgplugin_LTLIBRARIES = handlersocket.la
3592+handlersocket_la_LDFLAGS = -module ../libhsclient/libhsclient.la
3593+handlersocket_la_CFLAGS = $(MYSQL_INC) $(MYSQL_CFLAGS) $(AM_CFLAGS) \
3594+ -I../libhsclient
3595+handlersocket_la_CXXFLAGS = $(MYSQL_INC) $(MYSQL_CFLAGS) $(AM_CFLAGS) \
3596+ -I../libhsclient
3597+handlersocket_la_SOURCES = database.cpp handlersocket.cpp \
3598+ hstcpsvr_worker.cpp hstcpsvr.cpp
3599
3600=== added file 'HandlerSocket-Plugin-for-MySQL/handlersocket/Makefile.plain.template'
3601--- HandlerSocket-Plugin-for-MySQL/handlersocket/Makefile.plain.template 1970-01-01 00:00:00 +0000
3602+++ HandlerSocket-Plugin-for-MySQL/handlersocket/Makefile.plain.template 2011-04-12 04:16:24 +0000
3603@@ -0,0 +1,31 @@
3604+
3605+MYSQL_INC = HANDLERSOCKET_MYSQL_INC
3606+MYSQL_LIB = HANDLERSOCKET_MYSQL_LIB
3607+
3608+CXX = g++ -Wall -g -fno-rtti -fno-exceptions -fPIC -DPIC
3609+LIBS = $(MYSQL_LIB) -lhsclient -lpthread -lz
3610+CXXFLAGS = -I/usr/include/handlersocket $(MYSQL_INC)
3611+LDFLAGS =
3612+
3613+CXXFLAGS += -O3 -DNDEBUG
3614+
3615+HANDLERSOCKET_OBJS = database.o hstcpsvr.o hstcpsvr_worker.o
3616+
3617+all: handlersocket.so
3618+
3619+handlersocket.so: $(HANDLERSOCKET_OBJS) handlersocket.cpp
3620+ $(CXX) $(CXXFLAGS) -fno-strict-aliasing -shared $^ -o $@ $(LDFLAGS) \
3621+ -Wl,-soname -Wl,$@ $(LIBS)
3622+clean:
3623+ rm -f *.a *.so *.o
3624+
3625+LIBDIR = $(shell \
3626+ if [ -e /usr/lib64/mysql ]; then echo /usr/lib64; else echo /usr/lib; fi)
3627+
3628+install: handlersocket.so
3629+ sudo sh -c 'ulimit -c unlimited ; /etc/init.d/mysql stop ; \
3630+ cp handlersocket.so handlersocket.so.cpy && \
3631+ mv handlersocket.so.cpy \
3632+ $(LIBDIR)/mysql/plugin/handlersocket.so && \
3633+ /etc/init.d/mysql start'
3634+
3635
3636=== added file 'HandlerSocket-Plugin-for-MySQL/handlersocket/database.cpp'
3637--- HandlerSocket-Plugin-for-MySQL/handlersocket/database.cpp 1970-01-01 00:00:00 +0000
3638+++ HandlerSocket-Plugin-for-MySQL/handlersocket/database.cpp 2011-04-12 04:16:24 +0000
3639@@ -0,0 +1,1143 @@
3640+
3641+// vim:sw=2:ai
3642+
3643+/*
3644+ * Copyright (C) 2010 DeNA Co.,Ltd.. All rights reserved.
3645+ * See COPYRIGHT.txt for details.
3646+ */
3647+
3648+#include <stdlib.h>
3649+#include <stdio.h>
3650+#include <string.h>
3651+
3652+#include "database.hpp"
3653+#include "string_util.hpp"
3654+#include "escape.hpp"
3655+#include "mysql_incl.hpp"
3656+
3657+#define DBG_KEY(x)
3658+#define DBG_SHUT(x)
3659+#define DBG_LOCK(x)
3660+#define DBG_THR(x)
3661+#define DBG_CMP(x)
3662+#define DBG_FLD(x)
3663+#define DBG_FILTER(x)
3664+#define DBG_REFCNT(x)
3665+#define DBG_DELETED
3666+
3667+/* status variables */
3668+unsigned long long int open_tables_count;
3669+unsigned long long int close_tables_count;
3670+unsigned long long int lock_tables_count;
3671+unsigned long long int unlock_tables_count;
3672+unsigned long long int index_exec_count;
3673+
3674+namespace dena {
3675+
3676+prep_stmt::prep_stmt()
3677+ : dbctx(0), table_id(static_cast<size_t>(-1)),
3678+ idxnum(static_cast<size_t>(-1))
3679+{
3680+}
3681+prep_stmt::prep_stmt(dbcontext_i *c, size_t tbl, size_t idx,
3682+ const fields_type& rf, const fields_type& ff)
3683+ : dbctx(c), table_id(tbl), idxnum(idx), ret_fields(rf), filter_fields(ff)
3684+{
3685+ if (dbctx) {
3686+ dbctx->table_addref(table_id);
3687+ }
3688+}
3689+prep_stmt::~prep_stmt()
3690+{
3691+ if (dbctx) {
3692+ dbctx->table_release(table_id);
3693+ }
3694+}
3695+
3696+prep_stmt::prep_stmt(const prep_stmt& x)
3697+ : dbctx(x.dbctx), table_id(x.table_id), idxnum(x.idxnum),
3698+ ret_fields(x.ret_fields), filter_fields(x.filter_fields)
3699+{
3700+ if (dbctx) {
3701+ dbctx->table_addref(table_id);
3702+ }
3703+}
3704+
3705+prep_stmt&
3706+prep_stmt::operator =(const prep_stmt& x)
3707+{
3708+ if (this != &x) {
3709+ if (dbctx) {
3710+ dbctx->table_release(table_id);
3711+ }
3712+ dbctx = x.dbctx;
3713+ table_id = x.table_id;
3714+ idxnum = x.idxnum;
3715+ ret_fields = x.ret_fields;
3716+ filter_fields = x.filter_fields;
3717+ if (dbctx) {
3718+ dbctx->table_addref(table_id);
3719+ }
3720+ }
3721+ return *this;
3722+}
3723+
3724+struct database : public database_i, private noncopyable {
3725+ database(const config& c);
3726+ virtual ~database();
3727+ virtual dbcontext_ptr create_context(bool for_write) volatile;
3728+ virtual void stop() volatile;
3729+ virtual const config& get_conf() const volatile;
3730+ public:
3731+ int child_running;
3732+ private:
3733+ config conf;
3734+};
3735+
3736+struct tablevec_entry {
3737+ TABLE *table;
3738+ size_t refcount;
3739+ bool modified;
3740+ tablevec_entry() : table(0), refcount(0), modified(false) { }
3741+};
3742+
3743+struct expr_user_lock : private noncopyable {
3744+ expr_user_lock(THD *thd, int timeout)
3745+ : lck_key("handlersocket_wr", 16, &my_charset_latin1),
3746+ lck_timeout(timeout),
3747+ lck_func_get_lock(&lck_key, &lck_timeout),
3748+ lck_func_release_lock(&lck_key)
3749+ {
3750+ lck_key.fix_fields(thd, 0);
3751+ lck_timeout.fix_fields(thd, 0);
3752+ lck_func_get_lock.fix_fields(thd, 0);
3753+ lck_func_release_lock.fix_fields(thd, 0);
3754+ }
3755+ long long get_lock() {
3756+ return lck_func_get_lock.val_int();
3757+ }
3758+ long long release_lock() {
3759+ return lck_func_release_lock.val_int();
3760+ }
3761+ private:
3762+ Item_string lck_key;
3763+ Item_int lck_timeout;
3764+ Item_func_get_lock lck_func_get_lock;
3765+ Item_func_release_lock lck_func_release_lock;
3766+};
3767+
3768+struct dbcontext : public dbcontext_i, private noncopyable {
3769+ dbcontext(volatile database *d, bool for_write);
3770+ virtual ~dbcontext();
3771+ virtual void init_thread(const void *stack_botton,
3772+ volatile int& shutdown_flag);
3773+ virtual void term_thread();
3774+ virtual bool check_alive();
3775+ virtual void lock_tables_if();
3776+ virtual void unlock_tables_if();
3777+ virtual bool get_commit_error();
3778+ virtual void clear_error();
3779+ virtual void close_tables_if();
3780+ virtual void table_addref(size_t tbl_id);
3781+ virtual void table_release(size_t tbl_id);
3782+ virtual void cmd_open_index(dbcallback_i& cb, size_t pst_id, const char *dbn,
3783+ const char *tbl, const char *idx, const char *retflds,
3784+ const char *filflds);
3785+ virtual void cmd_exec_on_index(dbcallback_i& cb, const cmd_exec_args& args);
3786+ virtual void set_statistics(size_t num_conns, size_t num_active);
3787+ private:
3788+ int set_thread_message(const char *fmt, ...)
3789+ __attribute__((format (printf, 2, 3)));
3790+ bool parse_fields(TABLE *const table, const char *str,
3791+ prep_stmt::fields_type& flds);
3792+ void cmd_insert_internal(dbcallback_i& cb, const prep_stmt& pst,
3793+ const string_ref *fvals, size_t fvalslen);
3794+ void cmd_sql_internal(dbcallback_i& cb, const prep_stmt& pst,
3795+ const string_ref *fvals, size_t fvalslen);
3796+ void cmd_find_internal(dbcallback_i& cb, const prep_stmt& pst,
3797+ ha_rkey_function find_flag, const cmd_exec_args& args);
3798+ size_t calc_filter_buf_size(TABLE *table, const prep_stmt& pst,
3799+ const record_filter *filters);
3800+ bool fill_filter_buf(TABLE *table, const prep_stmt& pst,
3801+ const record_filter *filters, uchar *filter_buf, size_t len);
3802+ int check_filter(dbcallback_i& cb, TABLE *table, const prep_stmt& pst,
3803+ const record_filter *filters, const uchar *filter_buf);
3804+ void resp_record(dbcallback_i& cb, TABLE *const table, const prep_stmt& pst);
3805+ void dump_record(dbcallback_i& cb, TABLE *const table, const prep_stmt& pst);
3806+ int modify_record(dbcallback_i& cb, TABLE *const table,
3807+ const prep_stmt& pst, const cmd_exec_args& args, char mod_op,
3808+ size_t& modified_count);
3809+ private:
3810+ typedef std::vector<tablevec_entry> table_vec_type;
3811+ typedef std::pair<std::string, std::string> table_name_type;
3812+ typedef std::map<table_name_type, size_t> table_map_type;
3813+ private:
3814+ volatile database *const dbref;
3815+ bool for_write_flag;
3816+ THD *thd;
3817+ MYSQL_LOCK *lock;
3818+ bool lock_failed;
3819+ std::auto_ptr<expr_user_lock> user_lock;
3820+ int user_level_lock_timeout;
3821+ bool user_level_lock_locked;
3822+ bool commit_error;
3823+ std::vector<char> info_message_buf;
3824+ table_vec_type table_vec;
3825+ table_map_type table_map;
3826+ #if MYSQL_VERSION_ID >= 50505
3827+ MDL_request *mdl_request;
3828+ #else
3829+ void *mdl_request;
3830+ #endif
3831+};
3832+
3833+database::database(const config& c)
3834+ : child_running(1), conf(c)
3835+{
3836+}
3837+
3838+database::~database()
3839+{
3840+}
3841+
3842+dbcontext_ptr
3843+database::create_context(bool for_write) volatile
3844+{
3845+ return dbcontext_ptr(new dbcontext(this, for_write));
3846+}
3847+
3848+void
3849+database::stop() volatile
3850+{
3851+ child_running = false;
3852+}
3853+
3854+const config&
3855+database::get_conf() const volatile
3856+{
3857+ return const_cast<const config&>(conf);
3858+}
3859+
3860+database_ptr
3861+database_i::create(const config& conf)
3862+{
3863+ return database_ptr(new database(conf));
3864+}
3865+
3866+dbcontext::dbcontext(volatile database *d, bool for_write)
3867+ : dbref(d), for_write_flag(for_write), thd(0), lock(0), lock_failed(false),
3868+ user_level_lock_timeout(0), user_level_lock_locked(false),
3869+ commit_error(false), mdl_request(0)
3870+{
3871+ info_message_buf.resize(8192);
3872+ user_level_lock_timeout = d->get_conf().get_int("wrlock_timeout", 12);
3873+}
3874+
3875+dbcontext::~dbcontext()
3876+{
3877+}
3878+
3879+namespace {
3880+
3881+int
3882+wait_server_to_start(THD *thd, volatile int& shutdown_flag)
3883+{
3884+ int r = 0;
3885+ DBG_SHUT(fprintf(stderr, "HNDSOCK wsts\n"));
3886+ pthread_mutex_lock(&LOCK_server_started);
3887+ while (!mysqld_server_started) {
3888+ timespec abstime = { };
3889+ set_timespec(abstime, 1);
3890+ pthread_cond_timedwait(&COND_server_started, &LOCK_server_started,
3891+ &abstime);
3892+ pthread_mutex_unlock(&LOCK_server_started);
3893+ pthread_mutex_lock(&thd->mysys_var->mutex);
3894+ THD::killed_state st = thd->killed;
3895+ pthread_mutex_unlock(&thd->mysys_var->mutex);
3896+ DBG_SHUT(fprintf(stderr, "HNDSOCK wsts kst %d\n", (int)st));
3897+ pthread_mutex_lock(&LOCK_server_started);
3898+ if (st != THD::NOT_KILLED) {
3899+ DBG_SHUT(fprintf(stderr, "HNDSOCK wsts kst %d break\n", (int)st));
3900+ r = -1;
3901+ break;
3902+ }
3903+ if (shutdown_flag) {
3904+ DBG_SHUT(fprintf(stderr, "HNDSOCK wsts kst shut break\n"));
3905+ r = -1;
3906+ break;
3907+ }
3908+ }
3909+ pthread_mutex_unlock(&LOCK_server_started);
3910+ DBG_SHUT(fprintf(stderr, "HNDSOCK wsts done\n"));
3911+ return r;
3912+}
3913+
3914+}; // namespace
3915+
3916+void
3917+dbcontext::init_thread(const void *stack_bottom, volatile int& shutdown_flag)
3918+{
3919+ DBG_THR(fprintf(stderr, "HNDSOCK init thread\n"));
3920+ {
3921+ my_thread_init();
3922+ thd = new THD;
3923+ thd->thread_stack = (char *)stack_bottom;
3924+ DBG_THR(const size_t of = (char *)(&thd->thread_stack) - (char *)thd);
3925+ DBG_THR(fprintf(stderr, "thread_stack = %p sz=%zu of=%zu\n",
3926+ thd->thread_stack, sizeof(THD), of));
3927+ thd->store_globals();
3928+ thd->system_thread = static_cast<enum_thread_type>(1<<30UL);
3929+ const NET v = { 0 };
3930+ thd->net = v;
3931+ if (for_write_flag) {
3932+ #if MYSQL_VERSION_ID >= 50505
3933+ thd->variables.option_bits |= OPTION_BIN_LOG;
3934+ #else
3935+ thd->options |= OPTION_BIN_LOG;
3936+ #endif
3937+ safeFree(thd->db);
3938+ thd->db = 0;
3939+ thd->db = my_strdup("handlersocket", MYF(0));
3940+ }
3941+ my_pthread_setspecific_ptr(THR_THD, thd);
3942+ DBG_THR(fprintf(stderr, "HNDSOCK x0 %p\n", thd));
3943+ }
3944+ {
3945+ pthread_mutex_lock(&LOCK_thread_count);
3946+ thd->thread_id = thread_id++;
3947+ threads.append(thd);
3948+ ++thread_count;
3949+ pthread_mutex_unlock(&LOCK_thread_count);
3950+ }
3951+
3952+ DBG_THR(fprintf(stderr, "HNDSOCK init thread wsts\n"));
3953+ wait_server_to_start(thd, shutdown_flag);
3954+ DBG_THR(fprintf(stderr, "HNDSOCK init thread done\n"));
3955+
3956+ thd_proc_info(thd, &info_message_buf[0]);
3957+ set_thread_message("hs:listening");
3958+ DBG_THR(fprintf(stderr, "HNDSOCK x1 %p\n", thd));
3959+
3960+ #if MYSQL_VERSION_ID >= 50508
3961+ mdl_request = new(thd->mem_root) MDL_request;
3962+ mdl_request->init(MDL_key::TABLE, "", "",
3963+ for_write_flag ? MDL_SHARED_WRITE : MDL_SHARED_READ, MDL_STATEMENT);
3964+ #elif MYSQL_VERSION_ID >= 50505
3965+ mdl_request = MDL_request::create(MDL_key::TABLE, "", "",
3966+ for_write_flag ? MDL_SHARED_WRITE : MDL_SHARED_READ, thd->mem_root);
3967+ #endif
3968+
3969+ lex_start(thd);
3970+
3971+ user_lock.reset(new expr_user_lock(thd, user_level_lock_timeout));
3972+}
3973+
3974+int
3975+dbcontext::set_thread_message(const char *fmt, ...)
3976+{
3977+ va_list ap;
3978+ va_start(ap, fmt);
3979+ const int n = vsnprintf(&info_message_buf[0], info_message_buf.size(),
3980+ fmt, ap);
3981+ va_end(ap);
3982+ return n;
3983+}
3984+
3985+void
3986+dbcontext::term_thread()
3987+{
3988+ DBG_THR(fprintf(stderr, "HNDSOCK thread end %p\n", thd));
3989+ unlock_tables_if();
3990+ my_pthread_setspecific_ptr(THR_THD, 0);
3991+ {
3992+ pthread_mutex_lock(&LOCK_thread_count);
3993+ delete thd;
3994+ thd = 0;
3995+ --thread_count;
3996+ pthread_mutex_unlock(&LOCK_thread_count);
3997+ my_thread_end();
3998+ }
3999+}
4000+
4001+bool
4002+dbcontext::check_alive()
4003+{
4004+ pthread_mutex_lock(&thd->mysys_var->mutex);
4005+ THD::killed_state st = thd->killed;
4006+ pthread_mutex_unlock(&thd->mysys_var->mutex);
4007+ DBG_SHUT(fprintf(stderr, "chk HNDSOCK kst %p %p %d %zu\n", thd, &thd->killed,
4008+ (int)st, sizeof(*thd)));
4009+ if (st != THD::NOT_KILLED) {
4010+ DBG_SHUT(fprintf(stderr, "chk HNDSOCK kst %d break\n", (int)st));
4011+ return false;
4012+ }
4013+ return true;
4014+}
4015+
4016+void
4017+dbcontext::lock_tables_if()
4018+{
4019+ if (lock_failed) {
4020+ return;
4021+ }
4022+ if (for_write_flag && !user_level_lock_locked) {
4023+ if (user_lock->get_lock()) {
4024+ user_level_lock_locked = true;
4025+ } else {
4026+ lock_failed = true;
4027+ return;
4028+ }
4029+ }
4030+ if (lock == 0) {
4031+ const size_t num_max = table_vec.size();
4032+ TABLE *tables[num_max ? num_max : 1]; /* GNU */
4033+ size_t num_open = 0;
4034+ for (size_t i = 0; i < num_max; ++i) {
4035+ if (table_vec[i].refcount > 0) {
4036+ tables[num_open++] = table_vec[i].table;
4037+ }
4038+ table_vec[i].modified = false;
4039+ }
4040+ #if MYSQL_VERSION_ID >= 50505
4041+ lock = thd->lock = mysql_lock_tables(thd, &tables[0], num_open, 0);
4042+ #else
4043+ bool need_reopen= false;
4044+ lock = thd->lock = mysql_lock_tables(thd, &tables[0], num_open,
4045+ MYSQL_LOCK_NOTIFY_IF_NEED_REOPEN, &need_reopen);
4046+ #endif
4047+ statistic_increment(lock_tables_count, &LOCK_status);
4048+ thd_proc_info(thd, &info_message_buf[0]);
4049+ DENA_VERBOSE(100, fprintf(stderr, "HNDSOCK lock tables %p %p %zu %zu\n",
4050+ thd, lock, num_max, num_open));
4051+ if (lock == 0) {
4052+ lock_failed = true;
4053+ DENA_VERBOSE(10, fprintf(stderr, "HNDSOCK failed to lock tables %p\n",
4054+ thd));
4055+ }
4056+ if (for_write_flag) {
4057+ #if MYSQL_VERSION_ID >= 50505
4058+ thd->set_current_stmt_binlog_format_row();
4059+ #else
4060+ thd->current_stmt_binlog_row_based = 1;
4061+ #endif
4062+ }
4063+ }
4064+ DBG_LOCK(fprintf(stderr, "HNDSOCK tblnum=%d\n", (int)tblnum));
4065+}
4066+
4067+void
4068+dbcontext::unlock_tables_if()
4069+{
4070+ if (lock != 0) {
4071+ DENA_VERBOSE(100, fprintf(stderr, "HNDSOCK unlock tables\n"));
4072+ if (for_write_flag) {
4073+ for (size_t i = 0; i < table_vec.size(); ++i) {
4074+ if (table_vec[i].modified) {
4075+ query_cache_invalidate3(thd, table_vec[i].table, 0);
4076+ /* invalidate immediately */
4077+ }
4078+ }
4079+ bool suc = true;
4080+ #if MYSQL_VERSION_ID >= 50505
4081+ suc = (trans_commit_stmt(thd) == 0);
4082+ #else
4083+ suc = (ha_autocommit_or_rollback(thd, 0) == 0);
4084+ #endif
4085+ if (!suc) {
4086+ commit_error = true;
4087+ DENA_VERBOSE(10, fprintf(stderr,
4088+ "HNDSOCK unlock tables: commit failed\n"));
4089+ }
4090+ }
4091+ mysql_unlock_tables(thd, lock);
4092+ lock = thd->lock = 0;
4093+ statistic_increment(unlock_tables_count, &LOCK_status);
4094+ }
4095+ if (user_level_lock_locked) {
4096+ if (user_lock->release_lock()) {
4097+ user_level_lock_locked = false;
4098+ }
4099+ }
4100+}
4101+
4102+bool
4103+dbcontext::get_commit_error()
4104+{
4105+ return commit_error;
4106+}
4107+
4108+void
4109+dbcontext::clear_error()
4110+{
4111+ lock_failed = false;
4112+ commit_error = false;
4113+}
4114+
4115+void
4116+dbcontext::close_tables_if()
4117+{
4118+ unlock_tables_if();
4119+ if (!table_vec.empty()) {
4120+ DENA_VERBOSE(100, fprintf(stderr, "HNDSOCK close tables\n"));
4121+ close_thread_tables(thd);
4122+ statistic_increment(close_tables_count, &LOCK_status);
4123+ table_vec.clear();
4124+ table_map.clear();
4125+ }
4126+}
4127+
4128+void
4129+dbcontext::table_addref(size_t tbl_id)
4130+{
4131+ table_vec[tbl_id].refcount += 1;
4132+ DBG_REFCNT(fprintf(stderr, "%p %zu %zu addref\n", this, tbl_id,
4133+ table_vec[tbl_id].refcount));
4134+}
4135+
4136+void
4137+dbcontext::table_release(size_t tbl_id)
4138+{
4139+ table_vec[tbl_id].refcount -= 1;
4140+ DBG_REFCNT(fprintf(stderr, "%p %zu %zu release\n", this, tbl_id,
4141+ table_vec[tbl_id].refcount));
4142+}
4143+
4144+void
4145+dbcontext::resp_record(dbcallback_i& cb, TABLE *const table,
4146+ const prep_stmt& pst)
4147+{
4148+ char rwpstr_buf[64];
4149+ String rwpstr(rwpstr_buf, sizeof(rwpstr_buf), &my_charset_bin);
4150+ const prep_stmt::fields_type& rf = pst.get_ret_fields();
4151+ const size_t n = rf.size();
4152+ for (size_t i = 0; i < n; ++i) {
4153+ uint32_t fn = rf[i];
4154+ Field *const fld = table->field[fn];
4155+ DBG_FLD(fprintf(stderr, "fld=%p %zu\n", fld, fn));
4156+ if (fld->is_null()) {
4157+ /* null */
4158+ cb.dbcb_resp_entry(0, 0);
4159+ } else {
4160+ fld->val_str(&rwpstr, &rwpstr);
4161+ const size_t len = rwpstr.length();
4162+ if (len != 0) {
4163+ /* non-empty */
4164+ cb.dbcb_resp_entry(rwpstr.ptr(), rwpstr.length());
4165+ } else {
4166+ /* empty */
4167+ static const char empty_str[] = "";
4168+ cb.dbcb_resp_entry(empty_str, 0);
4169+ }
4170+ }
4171+ }
4172+}
4173+
4174+void
4175+dbcontext::dump_record(dbcallback_i& cb, TABLE *const table,
4176+ const prep_stmt& pst)
4177+{
4178+ char rwpstr_buf[64];
4179+ String rwpstr(rwpstr_buf, sizeof(rwpstr_buf), &my_charset_bin);
4180+ const prep_stmt::fields_type& rf = pst.get_ret_fields();
4181+ const size_t n = rf.size();
4182+ for (size_t i = 0; i < n; ++i) {
4183+ uint32_t fn = rf[i];
4184+ Field *const fld = table->field[fn];
4185+ if (fld->is_null()) {
4186+ /* null */
4187+ cb.dbcb_resp_entry(0, 0);
4188+ fprintf(stderr, "NULL");
4189+ } else {
4190+ fld->val_str(&rwpstr, &rwpstr);
4191+ const std::string s(rwpstr.ptr(), rwpstr.length());
4192+ fprintf(stderr, "[%s]", s.c_str());
4193+ }
4194+ }
4195+ fprintf(stderr, "\n");
4196+}
4197+
4198+int
4199+dbcontext::modify_record(dbcallback_i& cb, TABLE *const table,
4200+ const prep_stmt& pst, const cmd_exec_args& args, char mod_op,
4201+ size_t& modified_count)
4202+{
4203+ if (mod_op == 'U') {
4204+ /* update */
4205+ handler *const hnd = table->file;
4206+ uchar *const buf = table->record[0];
4207+ store_record(table, record[1]);
4208+ const prep_stmt::fields_type& rf = pst.get_ret_fields();
4209+ const size_t n = rf.size();
4210+ for (size_t i = 0; i < n; ++i) {
4211+ const string_ref& nv = args.uvals[i];
4212+ uint32_t fn = rf[i];
4213+ Field *const fld = table->field[fn];
4214+ if (nv.begin() == 0) {
4215+ fld->set_null();
4216+ } else {
4217+ fld->set_notnull();
4218+ fld->store(nv.begin(), nv.size(), &my_charset_bin);
4219+ }
4220+ }
4221+ table_vec[pst.get_table_id()].modified = true;
4222+ const int r = hnd->ha_update_row(table->record[1], buf);
4223+ if (r != 0 && r != HA_ERR_RECORD_IS_THE_SAME) {
4224+ return r;
4225+ }
4226+ ++modified_count; /* TODO: HA_ERR_RECORD_IS_THE_SAME? */
4227+ } else if (mod_op == 'D') {
4228+ /* delete */
4229+ handler *const hnd = table->file;
4230+ table_vec[pst.get_table_id()].modified = true;
4231+ const int r = hnd->ha_delete_row(table->record[0]);
4232+ if (r != 0) {
4233+ return r;
4234+ }
4235+ ++modified_count;
4236+ } else if (mod_op == '+' || mod_op == '-') {
4237+ /* increment/decrement */
4238+ handler *const hnd = table->file;
4239+ uchar *const buf = table->record[0];
4240+ store_record(table, record[1]);
4241+ const prep_stmt::fields_type& rf = pst.get_ret_fields();
4242+ const size_t n = rf.size();
4243+ size_t i = 0;
4244+ for (i = 0; i < n; ++i) {
4245+ const string_ref& nv = args.uvals[i];
4246+ uint32_t fn = rf[i];
4247+ Field *const fld = table->field[fn];
4248+ if (fld->is_null() || nv.begin() == 0) {
4249+ continue;
4250+ }
4251+ const long long pval = fld->val_int();
4252+ const long long llv = atoll_nocheck(nv.begin(), nv.end());
4253+ /* TODO: llv == 0? */
4254+ long long nval = 0;
4255+ if (mod_op == '+') {
4256+ /* increment */
4257+ nval = pval + llv;
4258+ } else {
4259+ /* decrement */
4260+ nval = pval - llv;
4261+ if ((pval < 0 && nval > 0) || (pval > 0 && nval < 0)) {
4262+ break; /* don't modify */
4263+ }
4264+ if ((pval < 0) != (nval < 0)) {
4265+ nval = 0; /* crip */
4266+ }
4267+ }
4268+ fld->store(nval, false);
4269+ }
4270+ if (i == n) {
4271+ /* modify */
4272+ table_vec[pst.get_table_id()].modified = true;
4273+ const int r = hnd->ha_update_row(table->record[1], buf);
4274+ if (r != 0 && r != HA_ERR_RECORD_IS_THE_SAME) {
4275+ return r;
4276+ }
4277+ ++modified_count;
4278+ }
4279+ }
4280+ return 0;
4281+}
4282+
4283+void
4284+dbcontext::cmd_insert_internal(dbcallback_i& cb, const prep_stmt& pst,
4285+ const string_ref *fvals, size_t fvalslen)
4286+{
4287+ if (!for_write_flag) {
4288+ return cb.dbcb_resp_short(2, "readonly");
4289+ }
4290+ lock_tables_if();
4291+ if (lock == 0) {
4292+ return cb.dbcb_resp_short(2, "lock_tables");
4293+ }
4294+ if (pst.get_table_id() >= table_vec.size()) {
4295+ return cb.dbcb_resp_short(2, "tblnum");
4296+ }
4297+ TABLE *const table = table_vec[pst.get_table_id()].table;
4298+ handler *const hnd = table->file;
4299+ uchar *const buf = table->record[0];
4300+ empty_record(table);
4301+ memset(buf, 0, table->s->null_bytes); /* clear null flags */
4302+ Field **fld = table->field;
4303+ size_t i = 0;
4304+ for (; *fld && i < fvalslen; ++fld, ++i) {
4305+ if (fvals[i].begin() == 0) {
4306+ (*fld)->set_null();
4307+ } else {
4308+ (*fld)->store(fvals[i].begin(), fvals[i].size(), &my_charset_bin);
4309+ }
4310+ }
4311+ table->next_number_field = table->found_next_number_field;
4312+ const int r = hnd->ha_write_row(buf);
4313+ table->next_number_field = 0;
4314+ table_vec[pst.get_table_id()].modified = true;
4315+ return cb.dbcb_resp_short(r != 0 ? 1 : 0, "");
4316+}
4317+
4318+void
4319+dbcontext::cmd_sql_internal(dbcallback_i& cb, const prep_stmt& pst,
4320+ const string_ref *fvals, size_t fvalslen)
4321+{
4322+ if (fvalslen < 1) {
4323+ return cb.dbcb_resp_short(2, "syntax");
4324+ }
4325+ return cb.dbcb_resp_short(2, "notimpl");
4326+}
4327+
4328+void
4329+dbcontext::cmd_find_internal(dbcallback_i& cb, const prep_stmt& pst,
4330+ ha_rkey_function find_flag, const cmd_exec_args& args)
4331+{
4332+ const bool debug_out = (verbose_level >= 100);
4333+ bool need_resp_record = true;
4334+ char mod_op = 0;
4335+ const string_ref& mod_op_str = args.mod_op;
4336+ if (mod_op_str.size() != 0) {
4337+ if (!for_write_flag) {
4338+ return cb.dbcb_resp_short(2, "readonly");
4339+ }
4340+ mod_op = mod_op_str.begin()[0];
4341+ need_resp_record = mod_op_str.size() > 1 && mod_op_str.begin()[1] == '?';
4342+ switch (mod_op) {
4343+ case 'U': /* update */
4344+ case 'D': /* delete */
4345+ case '+': /* increment */
4346+ case '-': /* decrement */
4347+ break;
4348+ default:
4349+ if (debug_out) {
4350+ fprintf(stderr, "unknown modop: %c\n", mod_op);
4351+ }
4352+ return cb.dbcb_resp_short(2, "modop");
4353+ }
4354+ }
4355+ lock_tables_if();
4356+ if (lock == 0) {
4357+ return cb.dbcb_resp_short(2, "lock_tables");
4358+ }
4359+ if (pst.get_table_id() >= table_vec.size()) {
4360+ return cb.dbcb_resp_short(2, "tblnum");
4361+ }
4362+ TABLE *const table = table_vec[pst.get_table_id()].table;
4363+ /* keys */
4364+ if (pst.get_idxnum() >= table->s->keys) {
4365+ return cb.dbcb_resp_short(2, "idxnum");
4366+ }
4367+ KEY& kinfo = table->key_info[pst.get_idxnum()];
4368+ if (args.kvalslen > kinfo.key_parts) {
4369+ return cb.dbcb_resp_short(2, "kpnum");
4370+ }
4371+ uchar key_buf[kinfo.key_length]; /* GNU */
4372+ size_t kplen_sum = 0;
4373+ {
4374+ DBG_KEY(fprintf(stderr, "SLOW\n"));
4375+ for (size_t i = 0; i < args.kvalslen; ++i) {
4376+ const KEY_PART_INFO & kpt = kinfo.key_part[i];
4377+ const string_ref& kval = args.kvals[i];
4378+ if (kval.begin() == 0) {
4379+ kpt.field->set_null();
4380+ } else {
4381+ kpt.field->set_notnull();
4382+ }
4383+ kpt.field->store(kval.begin(), kval.size(), &my_charset_bin);
4384+ kplen_sum += kpt.store_length;
4385+ }
4386+ key_copy(key_buf, table->record[0], &kinfo, kplen_sum);
4387+ }
4388+ /* filters */
4389+ uchar *filter_buf = 0;
4390+ if (args.filters != 0) {
4391+ const size_t filter_buf_len = calc_filter_buf_size(table, pst,
4392+ args.filters);
4393+ filter_buf = reinterpret_cast<uchar *>(alloca(filter_buf_len));
4394+ /* FIXME: TEST */
4395+ if (!fill_filter_buf(table, pst, args.filters, filter_buf,
4396+ filter_buf_len)) {
4397+ return cb.dbcb_resp_short(2, "filterblob");
4398+ }
4399+ }
4400+ /* handler */
4401+ table->read_set = &table->s->all_set;
4402+ handler *const hnd = table->file;
4403+ if (!for_write_flag) {
4404+ hnd->init_table_handle_for_HANDLER();
4405+ }
4406+ hnd->ha_index_or_rnd_end();
4407+ hnd->ha_index_init(pst.get_idxnum(), 1);
4408+ #if 0
4409+ statistic_increment(index_exec_count, &LOCK_status);
4410+ #endif
4411+ if (need_resp_record) {
4412+ cb.dbcb_resp_begin(pst.get_ret_fields().size());
4413+ }
4414+ const uint32_t limit = args.limit ? args.limit : 1;
4415+ uint32_t skip = args.skip;
4416+ size_t modified_count = 0;
4417+ int r = 0;
4418+ for (uint32_t i = 0; i < limit + skip; ++i) {
4419+ if (i == 0) {
4420+ const key_part_map kpm = (1U << args.kvalslen) - 1;
4421+ r = hnd->index_read_map(table->record[0], key_buf, kpm, find_flag);
4422+ } else {
4423+ switch (find_flag) {
4424+ case HA_READ_BEFORE_KEY:
4425+ case HA_READ_KEY_OR_PREV:
4426+ r = hnd->index_prev(table->record[0]);
4427+ break;
4428+ case HA_READ_AFTER_KEY:
4429+ case HA_READ_KEY_OR_NEXT:
4430+ r = hnd->index_next(table->record[0]);
4431+ break;
4432+ case HA_READ_KEY_EXACT:
4433+ r = hnd->index_next_same(table->record[0], key_buf, kplen_sum);
4434+ break;
4435+ default:
4436+ r = HA_ERR_END_OF_FILE; /* to finish the loop */
4437+ break;
4438+ }
4439+ }
4440+ if (debug_out) {
4441+ fprintf(stderr, "r=%d\n", r);
4442+ if (r == 0 || r == HA_ERR_RECORD_DELETED) {
4443+ dump_record(cb, table, pst);
4444+ }
4445+ }
4446+ int filter_res = 0;
4447+ if (r != 0) {
4448+ /* no-count */
4449+ } else if (args.filters != 0 && (filter_res = check_filter(cb, table,
4450+ pst, args.filters, filter_buf)) != 0) {
4451+ if (filter_res < 0) {
4452+ break;
4453+ }
4454+ } else if (skip > 0) {
4455+ --skip;
4456+ } else {
4457+ if (need_resp_record) {
4458+ resp_record(cb, table, pst);
4459+ }
4460+ if (mod_op != 0) {
4461+ r = modify_record(cb, table, pst, args, mod_op, modified_count);
4462+ }
4463+ }
4464+ if (r != 0 && r != HA_ERR_RECORD_DELETED) {
4465+ break;
4466+ }
4467+ }
4468+ hnd->ha_index_or_rnd_end();
4469+ if (r != 0 && r != HA_ERR_RECORD_DELETED && r != HA_ERR_KEY_NOT_FOUND &&
4470+ r != HA_ERR_END_OF_FILE) {
4471+ /* failed */
4472+ if (need_resp_record) {
4473+ /* revert dbcb_resp_begin() and dbcb_resp_entry() */
4474+ cb.dbcb_resp_cancel();
4475+ }
4476+ cb.dbcb_resp_short_num(2, r);
4477+ } else {
4478+ /* succeeded */
4479+ if (need_resp_record) {
4480+ cb.dbcb_resp_end();
4481+ } else {
4482+ cb.dbcb_resp_short_num(0, modified_count);
4483+ }
4484+ }
4485+}
4486+
4487+size_t
4488+dbcontext::calc_filter_buf_size(TABLE *table, const prep_stmt& pst,
4489+ const record_filter *filters)
4490+{
4491+ size_t filter_buf_len = 0;
4492+ for (const record_filter *f = filters; f->op.begin() != 0; ++f) {
4493+ if (f->val.begin() == 0) {
4494+ continue;
4495+ }
4496+ const uint32_t fn = pst.get_filter_fields()[f->ff_offset];
4497+ filter_buf_len += table->field[fn]->pack_length();
4498+ }
4499+ return filter_buf_len;
4500+}
4501+
4502+bool
4503+dbcontext::fill_filter_buf(TABLE *table, const prep_stmt& pst,
4504+ const record_filter *filters, uchar *filter_buf, size_t len)
4505+{
4506+ memset(filter_buf, 0, len);
4507+ size_t pos = 0;
4508+ for (const record_filter *f = filters; f->op.begin() != 0; ++f) {
4509+ if (f->val.begin() == 0) {
4510+ continue;
4511+ }
4512+ const uint32_t fn = pst.get_filter_fields()[f->ff_offset];
4513+ Field *const fld = table->field[fn];
4514+ if ((fld->flags & BLOB_FLAG) != 0) {
4515+ return false;
4516+ }
4517+ fld->store(f->val.begin(), f->val.size(), &my_charset_bin);
4518+ const size_t packlen = fld->pack_length();
4519+ memcpy(filter_buf + pos, fld->ptr, packlen);
4520+ pos += packlen;
4521+ }
4522+ return true;
4523+}
4524+
4525+int
4526+dbcontext::check_filter(dbcallback_i& cb, TABLE *table, const prep_stmt& pst,
4527+ const record_filter *filters, const uchar *filter_buf)
4528+{
4529+ DBG_FILTER(fprintf(stderr, "check_filter\n"));
4530+ size_t pos = 0;
4531+ for (const record_filter *f = filters; f->op.begin() != 0; ++f) {
4532+ const string_ref& op = f->op;
4533+ const string_ref& val = f->val;
4534+ const uint32_t fn = pst.get_filter_fields()[f->ff_offset];
4535+ Field *const fld = table->field[fn];
4536+ const size_t packlen = fld->pack_length();
4537+ const uchar *const bval = filter_buf + pos;
4538+ int cv = 0;
4539+ if (fld->is_null()) {
4540+ cv = (val.begin() == 0) ? 0 : -1;
4541+ } else {
4542+ cv = (val.begin() == 0) ? 1 : fld->cmp(bval);
4543+ }
4544+ DBG_FILTER(fprintf(stderr, "check_filter cv=%d\n", cv));
4545+ bool cond = true;
4546+ if (op.size() == 1) {
4547+ switch (op.begin()[0]) {
4548+ case '>':
4549+ DBG_FILTER(fprintf(stderr, "check_filter op: >\n"));
4550+ cond = (cv > 0);
4551+ break;
4552+ case '<':
4553+ DBG_FILTER(fprintf(stderr, "check_filter op: <\n"));
4554+ cond = (cv < 0);
4555+ break;
4556+ case '=':
4557+ DBG_FILTER(fprintf(stderr, "check_filter op: =\n"));
4558+ cond = (cv == 0);
4559+ break;
4560+ default:
4561+ DBG_FILTER(fprintf(stderr, "check_filter op: unknown\n"));
4562+ cond = false; /* FIXME: error */
4563+ break;
4564+ }
4565+ } else if (op.size() == 2 && op.begin()[1] == '=') {
4566+ switch (op.begin()[0]) {
4567+ case '>':
4568+ DBG_FILTER(fprintf(stderr, "check_filter op: >=\n"));
4569+ cond = (cv >= 0);
4570+ break;
4571+ case '<':
4572+ DBG_FILTER(fprintf(stderr, "check_filter op: <=\n"));
4573+ cond = (cv <= 0);
4574+ break;
4575+ case '!':
4576+ DBG_FILTER(fprintf(stderr, "check_filter op: !=\n"));
4577+ cond = (cv != 0);
4578+ break;
4579+ default:
4580+ DBG_FILTER(fprintf(stderr, "check_filter op: unknown\n"));
4581+ cond = false; /* FIXME: error */
4582+ break;
4583+ }
4584+ }
4585+ DBG_FILTER(fprintf(stderr, "check_filter cond: %d\n", (int)cond));
4586+ if (!cond) {
4587+ return (f->filter_type == record_filter_type_skip) ? 1 : -1;
4588+ }
4589+ if (val.begin() != 0) {
4590+ pos += packlen;
4591+ }
4592+ }
4593+ return 0;
4594+}
4595+
4596+void
4597+dbcontext::cmd_open_index(dbcallback_i& cb, size_t pst_id, const char *dbn,
4598+ const char *tbl, const char *idx, const char *retflds, const char *filflds)
4599+{
4600+ unlock_tables_if();
4601+ const table_name_type k = std::make_pair(std::string(dbn), std::string(tbl));
4602+ const table_map_type::const_iterator iter = table_map.find(k);
4603+ uint32_t tblnum = 0;
4604+ if (iter != table_map.end()) {
4605+ tblnum = iter->second;
4606+ DBG_CMP(fprintf(stderr, "HNDSOCK k=%s tblnum=%d\n", k.c_str(),
4607+ (int)tblnum));
4608+ } else {
4609+ TABLE_LIST tables;
4610+ TABLE *table = 0;
4611+ bool refresh = true;
4612+ const thr_lock_type lock_type = for_write_flag ? TL_WRITE : TL_READ;
4613+ #if MYSQL_VERSION_ID >= 50505
4614+ tables.init_one_table(dbn, strlen(dbn), tbl, strlen(tbl), tbl,
4615+ lock_type);
4616+ tables.mdl_request = mdl_request;
4617+ Open_table_context ot_act(thd, MYSQL_OPEN_REOPEN);
4618+ if (!open_table(thd, &tables, thd->mem_root, &ot_act)) {
4619+ table = tables.table;
4620+ }
4621+ #else
4622+ tables.init_one_table(dbn, tbl, lock_type);
4623+ table = open_table(thd, &tables, thd->mem_root, &refresh,
4624+ OPEN_VIEW_NO_PARSE);
4625+ #endif
4626+ if (table == 0) {
4627+ DENA_VERBOSE(10, fprintf(stderr,
4628+ "HNDSOCK failed to open %p [%s] [%s] [%d]\n",
4629+ thd, dbn, tbl, static_cast<int>(refresh)));
4630+ return cb.dbcb_resp_short(2, "open_table");
4631+ }
4632+ statistic_increment(open_tables_count, &LOCK_status);
4633+ table->reginfo.lock_type = lock_type;
4634+ table->use_all_columns();
4635+ tblnum = table_vec.size();
4636+ tablevec_entry e;
4637+ e.table = table;
4638+ table_vec.push_back(e);
4639+ table_map[k] = tblnum;
4640+ }
4641+ size_t idxnum = static_cast<size_t>(-1);
4642+ if (idx[0] >= '0' && idx[0] <= '9') {
4643+ /* numeric */
4644+ TABLE *const table = table_vec[tblnum].table;
4645+ idxnum = atoi(idx);
4646+ if (idxnum >= table->s->keys) {
4647+ return cb.dbcb_resp_short(2, "idxnum");
4648+ }
4649+ } else {
4650+ const char *const idx_name_to_open = idx[0] == '\0' ? "PRIMARY" : idx;
4651+ TABLE *const table = table_vec[tblnum].table;
4652+ for (uint i = 0; i < table->s->keys; ++i) {
4653+ KEY& kinfo = table->key_info[i];
4654+ if (strcmp(kinfo.name, idx_name_to_open) == 0) {
4655+ idxnum = i;
4656+ break;
4657+ }
4658+ }
4659+ }
4660+ if (idxnum == size_t(-1)) {
4661+ return cb.dbcb_resp_short(2, "idxnum");
4662+ }
4663+ prep_stmt::fields_type rf;
4664+ prep_stmt::fields_type ff;
4665+ if (!parse_fields(table_vec[tblnum].table, retflds, rf)) {
4666+ return cb.dbcb_resp_short(2, "fld");
4667+ }
4668+ if (!parse_fields(table_vec[tblnum].table, filflds, ff)) {
4669+ return cb.dbcb_resp_short(2, "fld");
4670+ }
4671+ prep_stmt p(this, tblnum, idxnum, rf, ff);
4672+ cb.dbcb_set_prep_stmt(pst_id, p);
4673+ return cb.dbcb_resp_short(0, "");
4674+}
4675+
4676+bool
4677+dbcontext::parse_fields(TABLE *const table, const char *str,
4678+ prep_stmt::fields_type& flds)
4679+{
4680+ string_ref flds_sr(str, strlen(str));
4681+ std::vector<string_ref> fldnms;
4682+ if (flds_sr.size() != 0) {
4683+ split(',', flds_sr, fldnms);
4684+ }
4685+ for (size_t i = 0; i < fldnms.size(); ++i) {
4686+ Field **fld = 0;
4687+ size_t j = 0;
4688+ for (fld = table->field; *fld; ++fld, ++j) {
4689+ DBG_FLD(fprintf(stderr, "f %s\n", (*fld)->field_name));
4690+ string_ref fn((*fld)->field_name, strlen((*fld)->field_name));
4691+ if (fn == fldnms[i]) {
4692+ break;
4693+ }
4694+ }
4695+ if (*fld == 0) {
4696+ DBG_FLD(fprintf(stderr, "UNKNOWN FLD %s [%s]\n", retflds,
4697+ std::string(fldnms[i].begin(), fldnms[i].size()).c_str()));
4698+ return false;
4699+ }
4700+ DBG_FLD(fprintf(stderr, "FLD %s %zu\n", (*fld)->field_name, j));
4701+ flds.push_back(j);
4702+ }
4703+ return true;
4704+}
4705+
4706+enum db_write_op {
4707+ db_write_op_none = 0,
4708+ db_write_op_insert = 1,
4709+ db_write_op_sql = 2,
4710+};
4711+
4712+void
4713+dbcontext::cmd_exec_on_index(dbcallback_i& cb, const cmd_exec_args& args)
4714+{
4715+ const prep_stmt& p = *args.pst;
4716+ if (p.get_table_id() == static_cast<size_t>(-1)) {
4717+ return cb.dbcb_resp_short(2, "stmtnum");
4718+ }
4719+ ha_rkey_function find_flag = HA_READ_KEY_EXACT;
4720+ db_write_op wrop = db_write_op_none;
4721+ if (args.op.size() == 1) {
4722+ switch (args.op.begin()[0]) {
4723+ case '=':
4724+ find_flag = HA_READ_KEY_EXACT;
4725+ break;
4726+ case '>':
4727+ find_flag = HA_READ_AFTER_KEY;
4728+ break;
4729+ case '<':
4730+ find_flag = HA_READ_BEFORE_KEY;
4731+ break;
4732+ case '+':
4733+ wrop = db_write_op_insert;
4734+ break;
4735+ case 'S':
4736+ wrop = db_write_op_sql;
4737+ break;
4738+ default:
4739+ return cb.dbcb_resp_short(1, "op");
4740+ }
4741+ } else if (args.op.size() == 2 && args.op.begin()[1] == '=') {
4742+ switch (args.op.begin()[0]) {
4743+ case '>':
4744+ find_flag = HA_READ_KEY_OR_NEXT;
4745+ break;
4746+ case '<':
4747+ find_flag = HA_READ_KEY_OR_PREV;
4748+ break;
4749+ default:
4750+ return cb.dbcb_resp_short(1, "op");
4751+ }
4752+ } else {
4753+ return cb.dbcb_resp_short(1, "op");
4754+ }
4755+ if (args.kvalslen <= 0) {
4756+ return cb.dbcb_resp_short(2, "klen");
4757+ }
4758+ switch (wrop) {
4759+ case db_write_op_none:
4760+ return cmd_find_internal(cb, p, find_flag, args);
4761+ case db_write_op_insert:
4762+ return cmd_insert_internal(cb, p, args.kvals, args.kvalslen);
4763+ case db_write_op_sql:
4764+ return cmd_sql_internal(cb, p, args.kvals, args.kvalslen);
4765+ }
4766+}
4767+
4768+void
4769+dbcontext::set_statistics(size_t num_conns, size_t num_active)
4770+{
4771+ thd_proc_info(thd, &info_message_buf[0]);
4772+ if (for_write_flag) {
4773+ set_thread_message("handlersocket: mode=wr, %zu conns, %zu active",
4774+ num_conns, num_active);
4775+ } else {
4776+ set_thread_message("handlersocket: mode=rd, %zu conns, %zu active",
4777+ num_conns, num_active);
4778+ }
4779+}
4780+
4781+};
4782+
4783
4784=== added file 'HandlerSocket-Plugin-for-MySQL/handlersocket/database.hpp'
4785--- HandlerSocket-Plugin-for-MySQL/handlersocket/database.hpp 1970-01-01 00:00:00 +0000
4786+++ HandlerSocket-Plugin-for-MySQL/handlersocket/database.hpp 2011-04-12 04:16:24 +0000
4787@@ -0,0 +1,130 @@
4788+
4789+// vim:sw=2:ai
4790+
4791+/*
4792+ * Copyright (C) 2010 DeNA Co.,Ltd.. All rights reserved.
4793+ * See COPYRIGHT.txt for details.
4794+ */
4795+
4796+#ifndef DENA_DATABASE_HPP
4797+#define DENA_DATABASE_HPP
4798+
4799+#include <string>
4800+#include <memory>
4801+#include <vector>
4802+#include <stdint.h>
4803+
4804+#include "string_buffer.hpp"
4805+#include "string_ref.hpp"
4806+#include "config.hpp"
4807+
4808+namespace dena {
4809+
4810+struct database_i;
4811+typedef std::auto_ptr<volatile database_i> database_ptr;
4812+
4813+struct dbcontext_i;
4814+typedef std::auto_ptr<dbcontext_i> dbcontext_ptr;
4815+
4816+struct database_i {
4817+ virtual ~database_i() { }
4818+ virtual dbcontext_ptr create_context(bool for_write) volatile = 0;
4819+ virtual void stop() volatile = 0;
4820+ virtual const config& get_conf() const volatile = 0;
4821+ static database_ptr create(const config& conf);
4822+};
4823+
4824+struct prep_stmt {
4825+ typedef std::vector<uint32_t> fields_type;
4826+ private:
4827+ dbcontext_i *dbctx; /* must be valid while *this is alive */
4828+ size_t table_id; /* a prep_stmt object holds a refcount of the table */
4829+ size_t idxnum;
4830+ fields_type ret_fields;
4831+ fields_type filter_fields;
4832+ public:
4833+ prep_stmt();
4834+ prep_stmt(dbcontext_i *c, size_t tbl, size_t idx, const fields_type& rf,
4835+ const fields_type& ff);
4836+ ~prep_stmt();
4837+ prep_stmt(const prep_stmt& x);
4838+ prep_stmt& operator =(const prep_stmt& x);
4839+ public:
4840+ size_t get_table_id() const { return table_id; }
4841+ size_t get_idxnum() const { return idxnum; }
4842+ const fields_type& get_ret_fields() const { return ret_fields; }
4843+ const fields_type& get_filter_fields() const { return filter_fields; }
4844+};
4845+
4846+struct dbcallback_i {
4847+ virtual ~dbcallback_i () { }
4848+ virtual void dbcb_set_prep_stmt(size_t pst_id, const prep_stmt& v) = 0;
4849+ virtual const prep_stmt *dbcb_get_prep_stmt(size_t pst_id) const = 0;
4850+ virtual void dbcb_resp_short(uint32_t code, const char *msg) = 0;
4851+ virtual void dbcb_resp_short_num(uint32_t code, uint32_t value) = 0;
4852+ virtual void dbcb_resp_begin(size_t num_flds) = 0;
4853+ virtual void dbcb_resp_entry(const char *fld, size_t fldlen) = 0;
4854+ virtual void dbcb_resp_end() = 0;
4855+ virtual void dbcb_resp_cancel() = 0;
4856+};
4857+
4858+enum record_filter_type {
4859+ record_filter_type_skip = 0,
4860+ record_filter_type_break = 1,
4861+};
4862+
4863+struct record_filter {
4864+ record_filter_type filter_type;
4865+ string_ref op;
4866+ uint32_t ff_offset; /* offset in filter_fields */
4867+ string_ref val;
4868+ record_filter() : filter_type(record_filter_type_skip), ff_offset(0) { }
4869+};
4870+
4871+struct cmd_exec_args {
4872+ const prep_stmt *pst;
4873+ string_ref op;
4874+ const string_ref *kvals;
4875+ size_t kvalslen;
4876+ uint32_t limit;
4877+ uint32_t skip;
4878+ string_ref mod_op;
4879+ const string_ref *uvals; /* size must be pst->retfieelds.size() */
4880+ const record_filter *filters;
4881+ cmd_exec_args() : pst(0), kvals(0), kvalslen(0), limit(0), skip(0),
4882+ uvals(0), filters(0) { }
4883+};
4884+
4885+struct dbcontext_i {
4886+ virtual ~dbcontext_i() { }
4887+ virtual void init_thread(const void *stack_bottom,
4888+ volatile int& shutdown_flag) = 0;
4889+ virtual void term_thread() = 0;
4890+ virtual bool check_alive() = 0;
4891+ virtual void lock_tables_if() = 0;
4892+ virtual void unlock_tables_if() = 0;
4893+ virtual bool get_commit_error() = 0;
4894+ virtual void clear_error() = 0;
4895+ virtual void close_tables_if() = 0;
4896+ virtual void table_addref(size_t tbl_id) = 0; /* TODO: hide */
4897+ virtual void table_release(size_t tbl_id) = 0; /* TODO: hide */
4898+ virtual void cmd_open_index(dbcallback_i& cb, size_t pst_id, const char *dbn,
4899+ const char *tbl, const char *idx, const char *retflds,
4900+ const char *filflds) = 0;
4901+ virtual void cmd_exec_on_index(dbcallback_i& cb, const cmd_exec_args& args)
4902+ = 0;
4903+ virtual void set_statistics(size_t num_conns, size_t num_active) = 0;
4904+};
4905+
4906+};
4907+
4908+extern unsigned long long int open_tables_count;
4909+extern unsigned long long int close_tables_count;
4910+extern unsigned long long int lock_tables_count;
4911+extern unsigned long long int unlock_tables_count;
4912+#if 0
4913+extern unsigned long long int index_exec_count;
4914+#endif
4915+
4916+#endif
4917+
4918
4919=== added file 'HandlerSocket-Plugin-for-MySQL/handlersocket/handlersocket.cpp'
4920--- HandlerSocket-Plugin-for-MySQL/handlersocket/handlersocket.cpp 1970-01-01 00:00:00 +0000
4921+++ HandlerSocket-Plugin-for-MySQL/handlersocket/handlersocket.cpp 2011-04-12 04:16:24 +0000
4922@@ -0,0 +1,216 @@
4923+
4924+// vim:sw=2:ai
4925+
4926+/*
4927+ * Copyright (C) 2010 DeNA Co.,Ltd.. All rights reserved.
4928+ * See COPYRIGHT.txt for details.
4929+ */
4930+
4931+#include <memory>
4932+#include <string>
4933+#include <stdio.h>
4934+
4935+#include "config.hpp"
4936+#include "hstcpsvr.hpp"
4937+#include "string_util.hpp"
4938+#include "mysql_incl.hpp"
4939+
4940+#define DBG_LOG \
4941+ if (dena::verbose_level >= 100) { \
4942+ fprintf(stderr, "%s %p\n", __PRETTY_FUNCTION__, this); \
4943+ }
4944+#define DBG_DO(x) if (dena::verbose_level >= 100) { x; }
4945+
4946+#define DBG_DIR(x)
4947+
4948+using namespace dena;
4949+
4950+static char *handlersocket_address = 0;
4951+static char *handlersocket_port = 0;
4952+static char *handlersocket_port_wr = 0;
4953+static unsigned int handlersocket_epoll = 1;
4954+static unsigned int handlersocket_threads = 32;
4955+static unsigned int handlersocket_threads_wr = 1;
4956+static unsigned int handlersocket_timeout = 30;
4957+static unsigned int handlersocket_backlog = 32768;
4958+static unsigned int handlersocket_sndbuf = 0;
4959+static unsigned int handlersocket_rcvbuf = 0;
4960+static unsigned int handlersocket_readsize = 0;
4961+static unsigned int handlersocket_accept_balance = 0;
4962+static unsigned int handlersocket_wrlock_timeout = 0;
4963+static char *handlersocket_plain_secret = 0;
4964+static char *handlersocket_plain_secret_wr = 0;
4965+
4966+struct daemon_handlersocket_data {
4967+ hstcpsvr_ptr hssvr_rd;
4968+ hstcpsvr_ptr hssvr_wr;
4969+};
4970+
4971+static int
4972+daemon_handlersocket_init(void *p)
4973+{
4974+ DENA_VERBOSE(10, fprintf(stderr, "handlersocket: initialized\n"));
4975+ config conf;
4976+ conf["use_epoll"] = handlersocket_epoll ? "1" : "0";
4977+ if (handlersocket_address) {
4978+ conf["host"] = handlersocket_address;
4979+ }
4980+ if (handlersocket_port) {
4981+ conf["port"] = handlersocket_port;
4982+ }
4983+ /*
4984+ * unix domain socket
4985+ * conf["host"] = "/";
4986+ * conf["port"] = "/tmp/handlersocket";
4987+ */
4988+ if (handlersocket_threads > 0) {
4989+ conf["num_threads"] = to_stdstring(handlersocket_threads);
4990+ } else {
4991+ conf["num_threads"] = "1";
4992+ }
4993+ conf["timeout"] = to_stdstring(handlersocket_timeout);
4994+ conf["listen_backlog"] = to_stdstring(handlersocket_backlog);
4995+ conf["sndbuf"] = to_stdstring(handlersocket_sndbuf);
4996+ conf["rcvbuf"] = to_stdstring(handlersocket_rcvbuf);
4997+ conf["readsize"] = to_stdstring(handlersocket_readsize);
4998+ conf["accept_balance"] = to_stdstring(handlersocket_accept_balance);
4999+ conf["wrlock_timeout"] = to_stdstring(handlersocket_wrlock_timeout);
5000+ std::auto_ptr<daemon_handlersocket_data> ap(new daemon_handlersocket_data);
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches