Commit 2c7d6f12 authored by Michael Widenius's avatar Michael Widenius

Added HandlerSocket plugin

- Fixed compiler errors
- Modified Makefiles to be part of plugin directory
- Some minor changes in database.cpp to use the new MariaDB handler interface
parent 26aa83bf
......@@ -1943,3 +1943,4 @@ storage/pbxt/bin/xtstat
libmysqld/sql_expression_cache.cc
mysql-test/mtr_command
scripts/convert-debug-for-diff
plugin/handler_socket/client/hsclient
......@@ -1291,3 +1291,34 @@ Use of any of this software is governed by the terms of the license below:
*/
***************************************************************************
%%The following software may be included in this product:
HandlerSocket plugin for MySQL
Copyright (c) 2010 DeNA Co.,Ltd.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of DeNA Co.,Ltd. nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY DeNA Co.,Ltd. "AS IS" AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
EVENT SHALL DeNA Co.,Ltd. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
***************************************************************************
Akira Higuchi (https://github.com/ahiguti)
- developed HanderSocket plugin, libhsclient, and perl-Net-HandlerSocket
Yoshinori Matsunobu (https://github.com/yoshinorim)
- introduced autotools, added support for MySQL 5.5.6, added statistics
variables
Jeff Hodges (https://github.com/jmhodges)
- fixed some autotools scripts
Toru Yamaguchi (https://github.com/zigorou)
- ported to MacOS X
Moriyoshi Koizumi (https://github.com/moriyoshi)
- fixed some autotools scripts
takeda-at (https://github.com/takada-at)
- added simple authorization function
1.0.6 - For MariaDB
* Modifications to Makefiles to be part of plugin directory
* Compiled by default in max builds
* Some minor changes in database.cpp to use the new MariaDB handler
interface
o * Fixed compiler warnings
1.0.6 - 2010-10-29
* Changed build instruction (autoreconf/configure/make), removed auto-generated files (Contributed by jmhodges)
*
1.0.5 - 2010-10-18
* Changed build procedures (using typical configure/make)
* Supported 5.5.6
* Added status variables
1.0.4 - 2010-08-15
* Initial public release
ACLOCAL_AMFLAGS = -I m4
SUBDIRS = @HANDLERSOCKET_SUBDIRS@
EXTRA_DIST= plug.in
perl:
cd perl-Net-HandlerSocket && perl Makefile.PL && make
install_perl:
cd perl-Net-HandlerSocket && make install
rpms: rpm_cli rpm_perl rpm_c
rpm_dir:
- mkdir dist
- mkdir dist/BUILD dist/RPMS dist/SOURCES dist/SPECS dist/SRPMS
rpm_cli: clean_cli rpm_dir
sed -e "s/HANDLERSOCKET_VERSION/$(VERSION)/" \
libhsclient/libhsclient.spec.template \
> libhsclient/libhsclient.spec
tar cvfz dist/libhsclient.tar.gz libhsclient
rpmbuild --define "_topdir `pwd`/dist" -ta \
dist/libhsclient.tar.gz
rpm_perl: clean_perl rpm_dir
sed -e "s/HANDLERSOCKET_VERSION/$(VERSION)/" \
perl-Net-HandlerSocket/perl-Net-HandlerSocket.spec.template \
> perl-Net-HandlerSocket/perl-Net-HandlerSocket.spec
cd perl-Net-HandlerSocket && perl Makefile.PL && make clean && \
rm -f Makefile.old
tar cvfz dist/perl-Net-HandlerSocket.tar.gz perl-Net-HandlerSocket
rpmbuild --define "_topdir `pwd`/dist" -ta \
dist/perl-Net-HandlerSocket.tar.gz
rpm_c: clean_c rpm_dir
sed -e "s/HANDLERSOCKET_VERSION/$(VERSION)/" \
handlersocket/handlersocket.spec.template \
> handlersocket/handlersocket.spec
sed -e "s|HANDLERSOCKET_MYSQL_INC|$(MYSQL_CFLAGS) $(MYSQL_INC)|" \
-e "s|HANDLERSOCKET_MYSQL_LIB|$(MYSQL_LIB)|" \
handlersocket/Makefile.plain.template \
> handlersocket/Makefile.plain
tar cvfz dist/handlersocket.tar.gz handlersocket
rpmbuild --define "_topdir `pwd`/dist" -ta \
dist/handlersocket.tar.gz
install_rpm_pl:
- sudo rpm -e perl-Net-HandlerSocket
- sudo rpm -e perl-Net-HandlerSocket-debuginfo
make clean
make rpm_perl
- sudo rpm -U dist/RPMS/*/perl*.rpm
installrpms:
- sudo rpm -e handlersocket
- sudo rpm -e handlersocket-debuginfo
- sudo rpm -e perl-Net-HandlerSocket
- sudo rpm -e perl-Net-HandlerSocket-debuginfo
- sudo rpm -e libhsclient
- sudo rpm -e libhsclient-debuginfo
make clean
make rpm_cli
- sudo rpm -U dist/RPMS/*/libhsclient*.rpm
make clean
make rpm_perl
- sudo rpm -U dist/RPMS/*/perl*.rpm
make clean
make rpm_c
- sudo rpm -U dist/RPMS/*/handlersocket*.rpm
clean_cli:
cd libhsclient && make clean
cd client && make clean
clean_perl:
cd perl-Net-HandlerSocket && perl Makefile.PL && make clean && \
rm -f Makefile.old
clean_c:
cd handlersocket && make clean
clean_all: clean_cli clean_perl clean_c
cd regtest && make clean
rm -rf dist/*/*
rm -f dist/*.tar.gz
Notes added by Monty:
This is HandlerSocket version 1.0.6 (See ChangeLog file)
The original code can be found at:
https://github.com/ahiguti/HandlerSocket-Plugin-for-MySQL
-----------------------------------------------------------------------------
HandlerSocket plugin for MySQL
Copyright (c) 2010 DeNA Co.,Ltd.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of DeNA Co.,Ltd. nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY DeNA Co.,Ltd. "AS IS" AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
EVENT SHALL DeNA Co.,Ltd. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-----------------------------------------------------------------------------
About HandlerSocket
HandlerSocket is a NoSQL plugin for MySQL. It works as a daemon inside the
mysqld process, accept tcp connections, and execute requests from clients.
HandlerSocket does not support SQL queries. Instead, it supports simple CRUD
operations on tables.
Because of the following reasons, HandlerSocket is much faster than the
mysqld/libmysql pair in some circumstances:
- HandlerSocket manipulates data without parsing SQL, which causes less
CPU usage.
- HandlerSocket reads many requests from clients and executes their
requests in bulk, which causes less CPU and disk usage.
- HandlerSocket client/server protocol is more compact than the
mysql/libmysql pair, which causes less network usage.
The current version of HandlerSocket only works with GNU/Linux. The source
archive of HandlerSocket includes a C++ and a Perl client libraries.
Here is a list of client libraries for other languages:
- PHP
http://openpear.org/package/Net_HandlerSocket
http://github.com/tz-lom/HSPHP
http://code.google.com/p/php-handlersocket/
- Java
http://code.google.com/p/hs4j/
http://code.google.com/p/handlersocketforjava/
- Python
http://pypi.python.org/pypi/python-handler-socket
https://code.launchpad.net/~songofacandy/+junk/pyhandlersocket
- Ruby
https://github.com/winebarrel/ruby-handlersocket
https://github.com/miyucy/handlersocket
- JavaScript
https://github.com/koichik/node-handlersocket
- Scala
https://github.com/fujohnwang/hs2client
The home of HandlerSocket is here:
https://github.com/ahiguti/HandlerSocket-Plugin-for-MySQL
More documents are available in docs-en/ and docs-ja/ directories.
#!/bin/sh
warn() {
echo -e "\tWARNING: $@" 1>&2
}
# init
LIBTOOLIZE=libtoolize
ACLOCAL=aclocal
AUTOCONF=autoconf
AUTOHEADER=autoheader
AUTOMAKE=automake
case `uname -s` in
Darwin)
LIBTOOLIZE=glibtoolize
;;
FreeBSD)
ACLOCAL_ARGS="$ACLOCAL_ARGS -I /usr/local/share/aclocal/"
;;
esac
# libtoolize
echo "Searching libtoolize..."
if [ `which $LIBTOOLIZE` ] ; then
echo -e "\tFOUND: libtoolize -> $LIBTOOLIZE"
else
warn "Cannot Found libtoolize... input libtool command"
read LIBTOOLIZE
LIBTOOLIZE=`which $LIBTOOLIZE`
if [ `which $LIBTOOLIZE` ] ; then
echo -e "\tSET: libtoolize -> $LIBTOOLIZE"
else
warn "$LIBTOOLIZE: Command not found."
exit 1;
fi
fi
# aclocal
echo "Searching aclocal..."
if [ `which $ACLOCAL` ] ; then
echo -e "\tFOUND: aclocal -> $ACLOCAL"
else
warn "Cannot Found aclocal... input aclocal command"
read ACLOCAL
ACLOCAL=`which $ACLOCAL`
if [ `which $ACLOCAL` ] ; then
echo -e "\tSET: aclocal -> $ACLOCAL"
else
warn "$ACLOCAL: Command not found."
exit 1;
fi
fi
# automake
echo "Searching automake..."
if [ `which $AUTOMAKE` ] ; then
echo -e "\tFOUND: automake -> $AUTOMAKE"
else
warn "Cannot Found automake... input automake command"
read AUTOMAKE
ACLOCAL=`which $AUTOMAKE`
if [ `which $AUTOMAKE` ] ; then
echo -e "\tSET: automake -> $AUTOMAKE"
else
warn "$AUTOMAKE: Command not found."
exit 1;
fi
fi
# autoheader
echo "Searching autoheader..."
if [ `which $AUTOHEADER` ] ; then
echo -e "\tFOUND: autoheader -> $AUTOHEADER"
else
warn "Cannot Found autoheader... input autoheader command"
read AUTOHEADER
ACLOCAL=`which $AUTOHEADER`
if [ `which $AUTOHEADER` ] ; then
echo -e "\tSET: autoheader -> $AUTOHEADER"
else
warn "$AUTOHEADER: Command not found."
exit 1;
fi
fi
# autoconf
echo "Searching autoconf..."
if [ `which $AUTOCONF` ] ; then
echo -e "\tFOUND: autoconf -> $AUTOCONF"
else
warn "Cannot Found autoconf... input autoconf command"
read AUTOCONF
ACLOCAL=`which $AUTOCONF`
if [ `which $AUTOCONF` ] ; then
echo -e "\tSET: autoconf -> $AUTOCONF"
else
warn "$AUTOCONF: Command not found."
exit 1;
fi
fi
echo "Running libtoolize ..."
$LIBTOOLIZE --force --copy
echo "Running aclocal ..."
$ACLOCAL ${ACLOCAL_ARGS} -I .
echo "Running autoheader..."
$AUTOHEADER
echo "Running automake ..."
$AUTOMAKE --add-missing --copy
echo "Running autoconf ..."
$AUTOCONF
mkdir m4 2> /dev/null
CXXFLAGS += -fimplicit-templates
AM_INCLUDES= -I$(srcdir)/../libhsclient
bin_PROGRAMS=hsclient
hsclient_SOURCES= hsclient.cpp
hsclient_LDFLAGS= -static -L../libhsclient -lhsclient
hsclient_CXXFLAGS= $(AM_INCLUDES)
hstest: hstest.o
$(CXX) $(CXXFLAGS) $(LFLAGS) hstest.o \
-L../libhsclient/.libs -lhsclient $(MYSQL_LIB) -o hstest
hstest.o: hstest.cpp
$(CXX) $(CXXFLAGS) $(MYSQL_INC) $(AM_INCLUDES) -c hstest.cpp
// vim:sw=2:ai
#include "hstcpcli.hpp"
#include "string_util.hpp"
namespace dena {
int
hstcpcli_main(int argc, char **argv)
{
config conf;
parse_args(argc, argv, conf);
socket_args sockargs;
sockargs.set(conf);
hstcpcli_ptr cli = hstcpcli_i::create(sockargs);
const std::string dbname = conf.get_str("dbname", "hstest");
const std::string table = conf.get_str("table", "hstest_table1");
const std::string index = conf.get_str("index", "PRIMARY");
const std::string fields = conf.get_str("fields", "k,v");
const int limit = conf.get_int("limit", 0);
const int skip = conf.get_int("skip", 0);
std::vector<std::string> keys;
std::vector<string_ref> keyrefs;
size_t num_keys = 0;
while (true) {
const std::string conf_key = std::string("k") + to_stdstring(num_keys);
const std::string k = conf.get_str(conf_key, "");
const std::string kx = conf.get_str(conf_key, "x");
if (k.empty() && kx == "x") {
break;
}
++num_keys;
keys.push_back(k);
}
for (size_t i = 0; i < keys.size(); ++i) {
const string_ref ref(keys[i].data(), keys[i].size());
keyrefs.push_back(ref);
}
const std::string op = conf.get_str("op", "=");
const string_ref op_ref(op.data(), op.size());
cli->request_buf_open_index(0, dbname.c_str(), table.c_str(),
index.c_str(), fields.c_str());
cli->request_buf_exec_generic(0, op_ref, num_keys == 0 ? 0 : &keyrefs[0],
num_keys, limit, skip, string_ref(), 0, 0);
int code = 0;
size_t numflds = 0;
do {
if (cli->request_send() != 0) {
fprintf(stderr, "request_send: %s\n", cli->get_error().c_str());
break;
}
if ((code = cli->response_recv(numflds)) != 0) {
fprintf(stderr, "response_recv: %s\n", cli->get_error().c_str());
break;
}
} while (false);
cli->response_buf_remove();
do {
if ((code = cli->response_recv(numflds)) != 0) {
fprintf(stderr, "response_recv: %s\n", cli->get_error().c_str());
break;
}
while (true) {
const string_ref *const row = cli->get_next_row();
if (row == 0) {
break;
}
printf("REC:");
for (size_t i = 0; i < numflds; ++i) {
const std::string val(row[i].begin(), row[i].size());
printf(" %s", val.c_str());
}
printf("\n");
}
} while (false);
cli->response_buf_remove();
return 0;
}
};
int
main(int argc, char **argv)
{
return dena::hstcpcli_main(argc, argv);
}
#!/usr/bin/perl
use strict;
use warnings;
use DB::HandlerSocket::Pool;
use DBI;
my %conf = ();
for my $i (@ARGV) {
my ($k, $v) = split(/=/, $i);
$conf{$k} = $v;
}
my $verbose = get_conf("verbose", 0);
my $actions_str = get_conf("actions",
"create,insert,verify,verify2,verify3,verify4,clean");
my $tablesize = get_conf("tablesize", 1000);
my $db = get_conf("db", "hstestdb");
my $table = get_conf("table", "testtbl");
my $table_schema = get_conf("table_schema", undef);
my $engine = get_conf("engine", "innodb");
my $host = get_conf("host", "localhost");
my $mysqlport = get_conf("mysqlport", 3306);
my $hsport_rd = get_conf("hsport_rd", 9998);
my $hsport_wr = get_conf("hsport_wr", 9999);
my $loop = get_conf("loop", 10000);
my $op = get_conf("op", "=");
my $ssps = get_conf("ssps", 0);
my $num_moreflds = get_conf("moreflds", 0);
my $moreflds_prefix = get_conf("moreflds_prefix", "f");
my $mysql_user = 'root';
my $mysql_password = '';
my $dsn = "DBI:mysql:database=;host=$host;port=$mysqlport"
. ";mysql_server_prepare=$ssps";
my $dbh = DBI->connect($dsn, $mysql_user, $mysql_password,
{ RaiseError => 1 });
my $hsargs = { 'host' => $host, 'port' => $hsport_rd };
my $hspool = new DB::HandlerSocket::Pool({
hostmap => {
"$db.$table" => {
host => $host,
port => $hsport_rd,
},
},
resolve => undef,
error => undef,
});
$table_schema = "(k int primary key, fc30 varchar(30), ft text)"
if (!defined($table_schema));
my @actions = split(/,/, $actions_str);
for my $action (@actions) {
print "ACTION: $action\n";
eval "hstest_$action()";
if ($@) {
die $@;
}
print "ACTION: $action DONE\n";
}
sub get_conf {
my ($key, $def) = @_;
my $val = $conf{$key};
if ($val) {
print "$key=$val\n";
} else {
$val = $def;
my $defstr = $def || "(undef)";
print "$key=$defstr(default)\n";
}
return $val;
}
sub hstest_create {
$dbh->do("drop database if exists $db");
$dbh->do("create database $db");
$dbh->do("use $db");
$dbh->do("create table $table $table_schema engine=$engine");
}
sub hstest_dump {
$dbh->do("use $db");
my $sth = $dbh->prepare("select * from $table");
$sth->execute();
my $arr = $sth->fetchall_arrayref();
for my $rec (@$arr) {
print "REC:";
for my $row (@$rec) {
print " $row";
}
print "\n";
}
}
sub hstest_insert {
$dbh->do("use $db");
my $sth = $dbh->prepare("insert into $table values (?, ?, ?)");
for (my $k = 0; $k < $tablesize; ++$k) {
my $fc30 = "fc30_$k";
my $ft = "ft_$k";
$sth->execute($k, $fc30, $ft);
}
}
sub hstest_verify {
$dbh->do("use $db");
my $sth = $dbh->prepare("select * from $table order by k");
$sth->execute();
my $arr = $sth->fetchall_arrayref();
my $hsres = $hspool->index_find($db, $table, "PRIMARY", "k,fc30,ft",
">=", [ 0 ], $tablesize, 0);
for (my $i = 0; $i < $tablesize; ++$i) {
my $rec = $arr->[$i];
my $differ = 0;
print "REC:" if $verbose;
for (my $j = 0; $j < 3; ++$j) {
my $fld = $rec->[$j];
my $hsidx = $i * 3 + $j;
my $hsfld = $hsres->[$hsidx];
if ($hsfld ne $fld) {
$differ = 1;
}
if ($differ) {
print " $fld:$hsfld" if $verbose;
} else {
print " $hsfld" if $verbose;
}
}
print "\n" if $verbose;
if ($differ) {
die "verification failed";
}
}
}
sub hstest_verify2 {
$dbh->do("use $db");
my $sth = $dbh->prepare("select * from $table order by k");
$sth->execute();
my $arr = $sth->fetchall_arrayref();
my $hsresa = $hspool->index_find_multi($db, $table, "PRIMARY",
"k,fc30,ft", [ [ -1, ">=", [ 0 ], $tablesize, 0 ] ]);
my $hsres = $hsresa->[0];
for (my $i = 0; $i < $tablesize; ++$i) {
my $rec = $arr->[$i];
my $differ = 0;
print "REC:" if $verbose;
for (my $j = 0; $j < 3; ++$j) {
my $fld = $rec->[$j];
my $hsidx = $i * 3 + $j;
my $hsfld = $hsres->[$hsidx];
if ($hsfld ne $fld) {
$differ = 1;
}
if ($differ) {
print " $fld:$hsfld" if $verbose;
} else {
print " $hsfld" if $verbose;
}
}
print "\n" if $verbose;
if ($differ) {
die "verification failed";
}
}
}
sub hashref_to_str {
my $href = $_[0];
my $r = '';
for my $k (sort keys %$href) {
my $v = $href->{$k};
$r .= "($k=>$v)";
}
return $r;
}
sub hstest_verify3 {
$dbh->do("use $db");
my $sth = $dbh->prepare("select * from $table order by k");
$sth->execute();
my $hsres_t = $hspool->index_find($db, $table, "PRIMARY", "k,fc30,ft",
">=", [ 0 ], $tablesize, 0);
my $hsres = DB::HandlerSocket::Pool::result_single_to_hasharr(
[ 'k', 'fc30', 'ft' ], $hsres_t);
for (my $i = 0; $i < $tablesize; ++$i) {
my $mystr = hashref_to_str($sth->fetchrow_hashref());
my $hsstr = hashref_to_str($hsres->[$i]);
if ($mystr ne $hsstr) {
print "DIFF my=[$mystr] hs=[$hsstr]\n" if $verbose;
die "verification failed";
} else {
print "OK $hsstr\n" if $verbose;
}
}
}
sub hstest_verify4 {
$dbh->do("use $db");
my $sth = $dbh->prepare("select * from $table order by k");
$sth->execute();
my $hsres_t = $hspool->index_find($db, $table, "PRIMARY", "k,fc30,ft",
">=", [ 0 ], $tablesize, 0);
my $hsres = DB::HandlerSocket::Pool::result_single_to_hashhash(
[ 'k', 'fc30', 'ft' ], 'k', $hsres_t);
my $rechash = $sth->fetchall_hashref('k');
while (my ($k, $href) = each (%$rechash)) {
my $mystr = hashref_to_str($href);
my $hsstr = hashref_to_str($hsres->{$k});
if ($mystr ne $hsstr) {
print "DIFF my=[$mystr] hs=[$hsstr]\n" if $verbose;
die "verification failed";
} else {
print "OK $hsstr\n" if $verbose;
}
}
}
sub hstest_clean {
$hspool->clear_pool();
$dbh->do("drop database if exists $db");
}
// vim:sw=2:ai
#include <signal.h>
#include <sys/time.h>
#include <stdio.h>
#include <string.h>
#include <vector>
#include <stdlib.h>
#include <memory>
#include <errno.h>
#include <mysql.h>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "util.hpp"
#include "auto_ptrcontainer.hpp"
#include "socket.hpp"
#include "thread.hpp"
#include "hstcpcli.hpp"
#if __GNUC__ >= 4
long atomic_exchange_and_add(volatile long *valp, long c)
{
return __sync_fetch_and_add(valp, c);
}
#else
#include <bits/atomicity.h>
using namespace __gnu_cxx;
long atomic_exchange_and_add(volatile long *valp, long c)
{
return __exchange_and_add((volatile _Atomic_word *)valp, c);
}
#endif
namespace dena {
struct auto_mysql : private noncopyable {
auto_mysql() : db(0) {
reset();
}
~auto_mysql() {
if (db) {
mysql_close(db);
}
}
void reset() {
if (db) {
mysql_close(db);
}
if ((db = mysql_init(0)) == 0) {
fatal_abort("failed to initialize mysql client");
}
}
operator MYSQL *() const { return db; }
private:
MYSQL *db;
};
struct auto_mysql_res : private noncopyable {
auto_mysql_res(MYSQL *db) {
res = mysql_store_result(db);
}
~auto_mysql_res() {
if (res) {
mysql_free_result(res);
}
}
operator MYSQL_RES *() const { return res; }
private:
MYSQL_RES *res;
};
struct auto_mysql_stmt : private noncopyable {
auto_mysql_stmt(MYSQL *db) {
stmt = mysql_stmt_init(db);
}
~auto_mysql_stmt() {
if (stmt) {
mysql_stmt_close(stmt);
}
}
operator MYSQL_STMT *() const { return stmt; }
private:
MYSQL_STMT *stmt;
};
namespace {
double
gettimeofday_double()
{
struct timeval tv = { };
if (gettimeofday(&tv, 0) != 0) {
fatal_abort("gettimeofday");
}
return static_cast<double>(tv.tv_usec) / 1000000 + tv.tv_sec;
}
// unused
void
wait_close(int fd)
{
char buf[1024];
while (true) {
int r = read(fd, buf, sizeof(buf));
if (r <= 0) {
break;
}
}
}
// unused
void
gentle_close(int fd)
{
int r = shutdown(fd, SHUT_WR);
if (r != 0) {
return;
}
wait_close(fd);
}
};
struct hstest_shared {
config conf;
socket_args arg;
int verbose;
size_t loop;
size_t pipe;
char op;
long num_threads;
mutable volatile long count;
mutable volatile long conn_count;
long wait_conn;
volatile char *keygen;
long keygen_size;
mutable volatile int enable_timing;
int usleep;
int dump;
hstest_shared() : verbose(0), loop(0), pipe(0), op('G'), num_threads(0),
count(0), conn_count(0), wait_conn(0), keygen(0), keygen_size(0),
enable_timing(0), usleep(0), dump(0) { }
void increment_count(unsigned int c = 1) const volatile {
atomic_exchange_and_add(&count, c);
}
void increment_conn(unsigned int c) const volatile {
atomic_exchange_and_add(&conn_count, c);
while (wait_conn != 0 && conn_count < wait_conn) {
sleep(1);
}
// fprintf(stderr, "wait_conn=%ld done\n", wait_conn);
}
};
struct hstest_thread {
struct arg_type {
size_t id;
const hstest_shared& sh;
bool watch_flag;
arg_type(size_t i, const hstest_shared& s, bool w)
: id(i), sh(s), watch_flag(w) { }
};
hstest_thread(const arg_type& a) : arg(a), io_success_count(0),
op_success_count(0), response_min(99999), response_max(0),
response_sum(0), response_avg(0) { }
void operator ()();
void test_1();
void test_2_3(int test_num);
void test_4_5(int test_num);
void test_6(int test_num);
void test_7(int test_num);
void test_8(int test_num);
void test_9(int test_num);
void test_10(int test_num);
void test_11(int test_num);
void test_12(int test_num);
void test_21(int test_num);
void test_22(int test_num);
void test_watch();
void sleep_if();
void set_timing(double time_spent);
arg_type arg;
auto_file fd;
size_t io_success_count;
size_t op_success_count;
double response_min, response_max, response_sum, response_avg;
};
void
hstest_thread::test_1()
{
char buf[1024];
unsigned int seed = arg.id;
seed ^= arg.sh.conf.get_int("seed_xor", 0);
std::string err;
if (socket_connect(fd, arg.sh.arg, err) != 0) {
fprintf(stderr, "connect: %d %s\n", errno, strerror(errno));
return;
}
const char op = arg.sh.op;
const int tablesize = arg.sh.conf.get_int("tablesize", 0);
for (size_t i = 0; i < arg.sh.loop; ++i) {
for (size_t j = 0; j < arg.sh.pipe; ++j) {
int k = 0, v = 0, len = 0;
if (op == 'G') {
k = rand_r(&seed);
v = rand_r(&seed); /* unused */
if (tablesize != 0) {
k &= tablesize;
}
len = snprintf(buf, sizeof(buf), "%c\tk%d\n", op, k);
} else {
k = rand_r(&seed);
v = rand_r(&seed);
if (tablesize != 0) {
k &= tablesize;
}
len = snprintf(buf, sizeof(buf), "%c\tk%d\tv%d\n", op, k, v);
}
const int wlen = write(fd.get(), buf, len);
if (wlen != len) {
return;
}
}
size_t read_cnt = 0;
size_t read_pos = 0;
while (read_cnt < arg.sh.pipe) {
const int rlen = read(fd.get(), buf + read_pos, sizeof(buf) - read_pos);
if (rlen <= 0) {
return;
}
read_pos += rlen;
while (true) {
const char *const p = static_cast<const char *>(memchr(buf, '\n',
read_pos));
if (p == 0) {
break;
}
++read_cnt;
++io_success_count;
arg.sh.increment_count();
if (p != buf && buf[0] == '=') {
++op_success_count;
}
const size_t rest_size = buf + read_pos - (p + 1);
if (rest_size != 0) {
memmove(buf, p + 1, rest_size);
}
read_pos = rest_size;
}
}
}
}
void
hstest_thread::test_2_3(int test_num)
{
#if 0
char buf_k[128], buf_v[128];
unsigned int seed = arg.id;
op_base_t op = static_cast<op_base_t>(arg.sh.op);
micli_ptr hnd;
if (test_num == 2) {
hnd = micli_i::create_remote(arg.sh.conf);
} else if (test_num == 3) {
// hnd = micli_i::create_inproc(arg.sh.localdb);
}
if (hnd.get() == 0) {
return;
}
for (size_t i = 0; i < arg.sh.loop; ++i) {
for (size_t j = 0; j < arg.sh.pipe; ++j) {
int k = 0, v = 0, klen = 0, vlen = 0;
k = rand_r(&seed);
klen = snprintf(buf_k, sizeof(buf_k), "k%d", k);
v = rand_r(&seed); /* unused */
vlen = snprintf(buf_v, sizeof(buf_v), "v%d", v);
string_ref arr[2];
arr[0] = string_ref(buf_k, klen);
arr[1] = string_ref(buf_v, vlen);
pstrarr_ptr rec(arr, 2);
if (hnd->execute(op, 0, 0, rec.get_const())) {
++io_success_count;
arg.sh.increment_count();
const dataset& res = hnd->get_result_ref();
if (res.size() == 1) {
++op_success_count;
}
}
}
}
#endif
}
void
hstest_thread::test_4_5(int test_num)
{
#if 0
char buf_k[128], buf_v[8192];
memset(buf_v, ' ', sizeof(buf_v));
unsigned int seed = arg.id;
op_base_t op = static_cast<op_base_t>(arg.sh.op);
micli_ptr hnd;
if (test_num == 4) {
hnd = micli_i::create_remote(arg.sh.conf);
} else if (test_num == 5) {
hnd = micli_i::create_inproc(arg.sh.localdb);
}
if (hnd.get() == 0) {
return;
}
for (size_t i = 0; i < arg.sh.loop; ++i) {
for (size_t j = 0; j < arg.sh.pipe; ++j) {
int k = 0, klen = 0, vlen = 0;
k = i & 0x0000ffffUL;
if (k == 0) {
fprintf(stderr, "k=0\n");
}
klen = snprintf(buf_k, sizeof(buf_k), "k%d", k);
vlen = rand_r(&seed) % 8192;
string_ref arr[2];
arr[0] = string_ref(buf_k, klen);
arr[1] = string_ref(buf_v, vlen);
pstrarr_ptr rec(arr, 2);
if (hnd->execute(op, 0, 0, rec.get_const())) {
++io_success_count;
const dataset& res = hnd->get_result_ref();
if (res.size() == 1) {
++op_success_count;
}
}
}
}
#endif
}
void
hstest_thread::test_6(int test_num)
{
int count = arg.sh.conf.get_int("count", 1);
auto_file fds[count];
for (int i = 0; i < count; ++i) {
const double t1 = gettimeofday_double();
std::string err;
if (socket_connect(fds[i], arg.sh.arg, err) != 0) {
fprintf(stderr, "id=%zu i=%d err=%s\n", arg.id, i, err.c_str());
}
const double t2 = gettimeofday_double();
if (t2 - t1 > 1) {
fprintf(stderr, "id=%zu i=%d time %f\n", arg.id, i, t2 - t1);
}
}
}
void
hstest_thread::test_7(int num)
{
/*
set foo 0 0 10
0123456789
STORED
get foo
VALUE foo 0 10
0123456789
END
get var
END
*/
char buf[1024];
const int keep_connection = arg.sh.conf.get_int("keep_connection", 1);
unsigned int seed = arg.id;
seed ^= arg.sh.conf.get_int("seed_xor", 0);
const int tablesize = arg.sh.conf.get_int("tablesize", 0);
const char op = arg.sh.op;
for (size_t i = 0; i < arg.sh.loop; ++i) {
const double tm1 = gettimeofday_double();
std::string err;
if (fd.get() < 0 && socket_connect(fd, arg.sh.arg, err) != 0) {
fprintf(stderr, "connect: %d %s\n", errno, strerror(errno));
return;
}
for (size_t j = 0; j < arg.sh.pipe; ++j) {
int k = 0, v = 0, len = 0;
if (op == 'G') {
k = rand_r(&seed);
v = rand_r(&seed); /* unused */
if (tablesize != 0) {
k &= tablesize;
}
len = snprintf(buf, sizeof(buf), "get k%d\r\n", k);
} else {
k = rand_r(&seed);
v = rand_r(&seed);
if (tablesize != 0) {
k &= tablesize;
}
char vbuf[1024];
int vlen = snprintf(vbuf, sizeof(vbuf),
"v%d"
// "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
// "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
// "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
// "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
// "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
, v);
len = snprintf(buf, sizeof(buf), "set k%d 0 0 %d\r\n%s\r\n",
k, vlen, vbuf);
}
const int wlen = write(fd.get(), buf, len);
if (wlen != len) {
return;
}
}
size_t read_cnt = 0;
size_t read_pos = 0;
bool read_response_done = false;
bool expect_value = false;
while (!read_response_done) {
const int rlen = read(fd.get(), buf + read_pos, sizeof(buf) - read_pos);
if (rlen <= 0) {
return;
}
read_pos += rlen;
while (true) {
const char *const p = static_cast<const char *>(memchr(buf, '\n',
read_pos));
if (p == 0) {
break;
}
++read_cnt;
if (expect_value) {
expect_value = false;
} else if (p >= buf + 6 && memcmp(buf, "VALUE ", 6) == 0) {
expect_value = true;
++op_success_count;
} else {
if (p == buf + 7 && memcmp(buf, "STORED\r", 7) == 0) {
++op_success_count;
}
read_response_done = true;
}
const size_t rest_size = buf + read_pos - (p + 1);
if (rest_size != 0) {
memmove(buf, p + 1, rest_size);
}
read_pos = rest_size;
}
++io_success_count;
}
arg.sh.increment_count();
if (!keep_connection) {
fd.close();
}
const double tm2 = gettimeofday_double();
set_timing(tm2 - tm1);
sleep_if();
}
}
struct rec {
std::string key;
std::string value;
};
void
hstest_thread::test_8(int test_num)
{
#if 0
char buf_k[128], buf_v[128];
unsigned int seed = arg.id;
// op_base_t op = static_cast<op_base_t>(arg.sh.op);
using namespace boost::multi_index;
typedef member<rec, std::string, &rec::key> rec_get_key;
typedef ordered_unique<rec_get_key> oui;
typedef multi_index_container< rec, indexed_by<oui> > mic;
#if 0
typedef std::map<std::string, std::string> m_type;
m_type m;
#endif
mic m;
for (size_t i = 0; i < arg.sh.loop; ++i) {
for (size_t j = 0; j < arg.sh.pipe; ++j) {
int k = 0, v = 0, klen = 0, vlen = 0;
k = rand_r(&seed);
klen = snprintf(buf_k, sizeof(buf_k), "k%d", k);
v = rand_r(&seed); /* unused */
vlen = snprintf(buf_v, sizeof(buf_v), "v%d", v);
const std::string ks(buf_k, klen);
const std::string vs(buf_v, vlen);
rec r;
r.key = ks;
r.value = vs;
m.insert(r);
// m.insert(std::make_pair(ks, vs));
++io_success_count;
++op_success_count;
arg.sh.increment_count();
}
}
#endif
}
struct mysqltest_thread_initobj : private noncopyable {
mysqltest_thread_initobj() {
mysql_thread_init();
}
~mysqltest_thread_initobj() {
mysql_thread_end();
}
};
void
hstest_thread::test_9(int test_num)
{
/* create table hstest
* ( k varchar(255) not null, v varchar(255) not null, primary key(k))
* engine = innodb; */
auto_mysql db;
// mysqltest_thread_initobj initobj;
std::string err;
const char op = arg.sh.op;
const std::string suffix = arg.sh.conf.get_str("value_suffix", "upd");
unsigned long long err_cnt = 0;
unsigned long long query_cnt = 0;
#if 0
my_bool reconnect = 0;
if (mysql_options(db, MYSQL_OPT_RECONNECT, &reconnect) != 0) {
err = "mysql_options() failed";
++err_cnt;
return;
}
#endif
unsigned int seed = time(0) + arg.id + 1;
seed ^= arg.sh.conf.get_int("seed_xor", 0);
drand48_data randbuf;
srand48_r(seed, &randbuf);
const std::string mysql_host = arg.sh.conf.get_str("host", "localhost");
const int mysql_port = arg.sh.conf.get_int("mysqlport", 3306);
const int num = arg.sh.loop;
const std::string mysql_user = arg.sh.conf.get_str("mysqluser", "root");
const std::string mysql_passwd = arg.sh.conf.get_str("mysqlpass", "");
const std::string mysql_dbname = arg.sh.conf.get_str("dbname", "hstest");
const int keep_connection = arg.sh.conf.get_int("keep_connection", 1);
const int verbose = arg.sh.conf.get_int("verbose", 1);
const int tablesize = arg.sh.conf.get_int("tablesize", 10000);
const int moreflds = arg.sh.conf.get_int("moreflds", 0);
const std::string moreflds_prefix = arg.sh.conf.get_str(
"moreflds_prefix", "column0123456789_");
const int use_handler = arg.sh.conf.get_int("handler", 0);
const int sched_flag = arg.sh.conf.get_int("sched", 0);
const int ssps = arg.sh.conf.get_int("ssps", 0);
std::string flds = "v";
for (int i = 0; i < moreflds; ++i) {
char buf[1024];
snprintf(buf, sizeof(buf), ",%s%d", moreflds_prefix.c_str(), i);
flds += std::string(buf);
}
int connected = 0;
std::auto_ptr<auto_mysql_stmt> stmt;
for (int i = 0; i < num; ++i) {
const double tm1 = gettimeofday_double();
const int flags = 0;
if (connected == 0) {
if (!mysql_real_connect(db, mysql_host.c_str(),
mysql_user.c_str(), mysql_user.empty() ? 0 : mysql_passwd.c_str(),
mysql_dbname.c_str(), mysql_port, 0, flags)) {
err = "failed to connect: " + std::string(mysql_error(db));
if (verbose >= 1) {
fprintf(stderr, "e=[%s]\n", err.c_str());
}
++err_cnt;
return;
}
arg.sh.increment_conn(1);
}
int r = 0;
if (connected == 0 && use_handler) {
const char *const q = "handler hstest_table1 open";
r = mysql_real_query(db, q, strlen(q));
if (r != 0) {
err = 1;
}
}
if (connected == 0 && ssps) {
stmt.reset(new auto_mysql_stmt(db));
const char *const q = "select v from hstest_table1 where k = ?";
r = mysql_stmt_prepare(*stmt, q, strlen(q));
if (r != 0) {
fprintf(stderr, "ssps err\n");
++err_cnt;
return;
}
}
connected = 1;
std::string result_str;
unsigned int err = 0;
unsigned int num_flds = 0, num_affected_rows = 0;
int got_data = 0;
char buf_query[16384];
int buf_query_len = 0;
int k = 0, v = 0;
{
double kf = 0, vf = 0;
drand48_r(&randbuf, &kf);
drand48_r(&randbuf, &vf);
k = int(kf * tablesize);
v = int(vf * tablesize);
#if 0
k = rand_r(&seed);
v = rand_r(&seed);
if (tablesize != 0) {
k %= tablesize;
}
#endif
if (op == 'G') {
if (use_handler) {
buf_query_len = snprintf(buf_query, sizeof(buf_query),
"handler hstest_table1 read `primary` = ( '%d' )", k);
// TODO: moreflds
} else if (ssps) {
//
} else {
buf_query_len = snprintf(buf_query, sizeof(buf_query),
"select %s from hstest_table1 where k = '%d'", flds.c_str(), k);
}
} else if (op == 'U') {
buf_query_len = snprintf(buf_query, sizeof(buf_query),
"update hstest_table1 set v = '%d_%d%s' where k = '%d'",
v, k, suffix.c_str(), k);
} else if (op == 'R') {
buf_query_len = snprintf(buf_query, sizeof(buf_query),
"replace into hstest_table1 values ('%d', 'v%d')", k, v);
// TODO: moreflds
}
}
if (r == 0) {
if (ssps) {
MYSQL_BIND bind[1] = { };
bind[0].buffer_type = MYSQL_TYPE_LONG;
bind[0].buffer = (char *)&k;
bind[0].is_null = 0;
bind[0].length = 0;
if (mysql_stmt_bind_param(*stmt, bind)) {
fprintf(stderr, "err: %s\n", mysql_stmt_error(*stmt));
++err_cnt;
return;
}
r = mysql_stmt_execute(*stmt);
// fprintf(stderr, "stmt exec\n");
} else {
r = mysql_real_query(db, buf_query, buf_query_len);
// fprintf(stderr, "real query\n");
}
++query_cnt;
}
if (r != 0) {
err = 1;
} else if (ssps) {
if (verbose >= 0) {
char resbuf[1024];
unsigned long res_len = 0;
MYSQL_BIND bind[1] = { };
bind[0].buffer_type = MYSQL_TYPE_STRING;
bind[0].buffer = resbuf;
bind[0].buffer_length = sizeof(resbuf);
bind[0].length = &res_len;
if (mysql_stmt_bind_result(*stmt, bind)) {
fprintf(stderr, "err: %s\n", mysql_stmt_error(*stmt));
++err_cnt;
return;
}
if (mysql_stmt_fetch(*stmt)) {
fprintf(stderr, "err: %s\n", mysql_stmt_error(*stmt));
++err_cnt;
return;
}
if (!result_str.empty()) {
result_str += " ";
}
result_str += std::string(resbuf, res_len);
// fprintf(stderr, "SSPS RES: %s\n", result_str.c_str());
got_data = 1;
} else {
got_data = 1;
}
} else {
auto_mysql_res res(db);
if (res != 0) {
if (verbose >= 0) {
num_flds = mysql_num_fields(res);
MYSQL_ROW row = 0;
while ((row = mysql_fetch_row(res)) != 0) {
got_data = 1;
unsigned long *const lengths = mysql_fetch_lengths(res);
if (verbose >= 2) {
for (unsigned int i = 0; i < num_flds; ++i) {
if (!result_str.empty()) {
result_str += " ";
}
result_str += std::string(row[i], lengths[i]);
}
}
}
} else {
got_data = 1;
}
} else {
if (mysql_field_count(db) == 0) {
num_affected_rows = mysql_affected_rows(db);
} else {
err = 1;
}
}
}
if (verbose >= 2 || (verbose >= 1 && err != 0)) {
if (err) {
++err_cnt;
const char *const errstr = mysql_error(db);
fprintf(stderr, "e=[%s] a=%u q=[%s]\n", errstr,
num_affected_rows, buf_query);
} else {
fprintf(stderr, "a=%u q=[%s] r=[%s]\n", num_affected_rows, buf_query,
result_str.c_str());
}
}
if (err == 0) {
++io_success_count;
if (num_affected_rows > 0 || got_data > 0) {
++op_success_count;
} else {
if (verbose >= 1) {
fprintf(stderr, "k=%d numaff=%u gotdata=%d\n",
k, num_affected_rows, got_data);
}
}
arg.sh.increment_count();
}
if (!keep_connection) {
if (stmt.get() != 0) {
stmt.reset();
}
db.reset();
connected = 0;
}
const double tm2 = gettimeofday_double();
set_timing(tm2 - tm1);
sleep_if();
if (sched_flag) {
sched_yield();
}
}
if (verbose >= 1) {
fprintf(stderr, "thread finished (error_count=%llu)\n", err_cnt);
}
}
void
hstest_thread::test_10(int test_num)
{
const int keep_connection = arg.sh.conf.get_int("keep_connection", 1);
unsigned int seed = time(0) + arg.id + 1;
seed ^= arg.sh.conf.get_int("seed_xor", 0);
drand48_data randbuf;
srand48_r(seed, &randbuf);
std::string err;
int keepconn_count = 0;
const char op = arg.sh.op;
const int verbose = arg.sh.conf.get_int("verbose", 1);
const std::string suffix = arg.sh.conf.get_str("value_suffix", "upd");
const int tablesize = arg.sh.conf.get_int("tablesize", 10000);
const int firstkey = arg.sh.conf.get_int("firstkey", 0);
const int sched_flag = arg.sh.conf.get_int("sched", 0);
const int moreflds = arg.sh.conf.get_int("moreflds", 0);
const std::string dbname = arg.sh.conf.get_str("dbname", "hstest");
const std::string table = arg.sh.conf.get_str("table", "hstest_table1");
const std::string index = arg.sh.conf.get_str("index", "PRIMARY");
const std::string field = arg.sh.conf.get_str("field", "v");
const std::string moreflds_prefix = arg.sh.conf.get_str(
"moreflds_prefix", "column0123456789_");
const int dump = arg.sh.dump;
const int nodup = arg.sh.conf.get_int("nodup", 0);
std::string moreflds_str;
for (int i = 0; i < moreflds; ++i) {
char sbuf[1024];
snprintf(sbuf, sizeof(sbuf), ",%s%d", moreflds_prefix.c_str(), i);
moreflds_str += std::string(sbuf);
}
char wbuf[16384], rbuf[16384];
int wbuflen = 0;
for (size_t i = 0; i < arg.sh.loop; ++i) {
int len = 0, rlen = 0, wlen = 0;
#if 0
const double tm1 = gettimeofday_double();
#endif
if (fd.get() < 0) {
if (socket_connect(fd, arg.sh.arg, err) != 0) {
fprintf(stderr, "connect: %d %s\n", errno, strerror(errno));
return;
}
len = snprintf(wbuf, sizeof(wbuf),
"P\t1\t%s\t%s\tPRIMARY\t%s%s\n", dbname.c_str(), table.c_str(),
field.c_str(), moreflds_str.c_str());
/* pst_num, db, table, index, retflds */
wlen = write(fd.get(), wbuf, len);
if (len != wlen) {
fprintf(stderr, "write: %d %d\n", len, wlen);
return;
}
rlen = read(fd.get(), rbuf, sizeof(rbuf));
if (rlen <= 0 || rbuf[rlen - 1] != '\n') {
fprintf(stderr, "read: rlen=%d errno=%d\n", rlen, errno);
return;
}
if (rbuf[0] != '0') {
fprintf(stderr, "failed to open table\n");
return;
}
arg.sh.increment_conn(1);
}
const double tm1 = gettimeofday_double();
for (size_t j = 0; j < arg.sh.pipe; ++j) {
int k = 0, v = 0;
wbuflen = 0;
{
while (true) {
double kf = 0, vf = 0;
drand48_r(&randbuf, &kf);
drand48_r(&randbuf, &vf);
k = int(kf * tablesize) + firstkey;
v = int(vf * tablesize) + firstkey;
// k = rand_r(&seed);
// v = rand_r(&seed); /* unused */
#if 0
if (tablesize != 0) {
k &= tablesize;
}
#endif
if (op == 'G') {
wbuflen = snprintf(wbuf, sizeof(wbuf), "1\t=\t1\t%d\n", k);
} else if (op == 'U') {
wbuflen = snprintf(wbuf, sizeof(wbuf),
"1\t=\t1\t%d\t1\t0\tU\t%d_%d%s\n", k, v, k, suffix.c_str());
}
if (k - firstkey < arg.sh.keygen_size) {
volatile char *const ptr = arg.sh.keygen + (k - firstkey);
// int oldv = __sync_fetch_and_or(ptr, 1);
int oldv = *ptr;
*ptr += 1;
if (nodup && oldv != 0) {
if (dump) {
fprintf(stderr, "retry\n");
}
continue;
}
} else {
if (nodup) {
if (dump) {
fprintf(stderr, "retry2\n");
}
continue;
}
}
break;
}
}
wlen = write(fd.get(), wbuf, wbuflen);
if (wlen != wbuflen) {
fprintf(stderr, "write: %d %d\n", wbuflen, wlen);
return;
}
}
size_t read_cnt = 0;
size_t read_pos = 0;
while (read_cnt < arg.sh.pipe) {
rlen = read(fd.get(), rbuf + read_pos, sizeof(rbuf) - read_pos);
if (rlen <= 0) {
fprintf(stderr, "read: %d\n", rlen);
return;
}
read_pos += rlen;
while (true) {
const char *const nl = static_cast<const char *>(memchr(rbuf, '\n',
read_pos));
if (nl == 0) {
break;
}
++read_cnt;
++io_success_count;
const char *t1 = static_cast<const char *>(memchr(rbuf, '\t',
nl - rbuf));
if (t1 == 0) {
fprintf(stderr, "error \n");
break;
}
++t1;
const char *t2 = static_cast<const char *>(memchr(t1, '\t',
nl - t1));
if (t2 == 0) {
if (verbose > 1) {
fprintf(stderr, "key: notfound \n");
}
break;
}
++t2;
if (t1 == rbuf + 2 && rbuf[0] == '0') {
if (op == 'G') {
++op_success_count;
arg.sh.increment_count();
} else if (op == 'U') {
const char *t3 = t2;
while (t3 != nl && t3[0] >= 0x10) {
++t3;
}
if (t3 != t2 + 1 || t2[0] != '1') {
const std::string mess(t2, t3);
fprintf(stderr, "mod: %s\n", mess.c_str());
} else {
++op_success_count;
arg.sh.increment_count();
if (arg.sh.dump && arg.sh.pipe == 1) {
fwrite(wbuf, wbuflen, 1, stderr);
}
}
}
} else {
const char *t3 = t2;
while (t3 != nl && t3[0] >= 0x10) {
++t3;
}
const std::string mess(t2, t3);
fprintf(stderr, "err: %s\n", mess.c_str());
}
const size_t rest_size = rbuf + read_pos - (nl + 1);
if (rest_size != 0) {
memmove(rbuf, nl + 1, rest_size);
}
read_pos = rest_size;
}
}
if (!keep_connection) {
fd.reset();
arg.sh.increment_conn(-1);
} else if (keep_connection > 1 && ++keepconn_count > keep_connection) {
keepconn_count = 0;
fd.reset();
arg.sh.increment_conn(-1);
}
const double tm2 = gettimeofday_double();
set_timing(tm2 - tm1);
sleep_if();
if (sched_flag) {
sched_yield();
}
}
if (dump) {
fprintf(stderr, "done\n");
}
}
void
hstest_thread::sleep_if()
{
if (arg.sh.usleep) {
struct timespec ts = {
arg.sh.usleep / 1000000,
(arg.sh.usleep % 1000000) * 1000
};
nanosleep(&ts, 0);
}
}
void
hstest_thread::set_timing(double time_spent)
{
response_min = std::min(response_min, time_spent);
response_max = std::max(response_max, time_spent);
response_sum += time_spent;
if (op_success_count != 0) {
response_avg = response_sum / op_success_count;
}
}
void
hstest_thread::test_11(int test_num)
{
const int keep_connection = arg.sh.conf.get_int("keep_connection", 1);
const int tablesize = arg.sh.conf.get_int("tablesize", 0);
unsigned int seed = arg.id;
seed ^= arg.sh.conf.get_int("seed_xor", 0);
std::string err;
hstcpcli_ptr cli;
for (size_t i = 0; i < arg.sh.loop; ++i) {
if (cli.get() == 0) {
cli = hstcpcli_i::create(arg.sh.arg);
cli->request_buf_open_index(0, "hstest", "hstest_table1", "", "v");
/* pst_num, db, table, index, retflds */
if (cli->request_send() != 0) {
fprintf(stderr, "reuqest_send: %s\n", cli->get_error().c_str());
return;
}
size_t num_flds = 0;
if (cli->response_recv(num_flds) != 0) {
fprintf(stderr, "reuqest_recv: %s\n", cli->get_error().c_str());
return;
}
cli->response_buf_remove();
}
for (size_t j = 0; j < arg.sh.pipe; ++j) {
char buf[256];
int k = 0, v = 0, len = 0;
{
k = rand_r(&seed);
v = rand_r(&seed); /* unused */
if (tablesize != 0) {
k &= tablesize;
}
len = snprintf(buf, sizeof(buf), "%d", k);
}
const string_ref key(buf, len);
const string_ref op("=", 1);
cli->request_buf_exec_generic(0, op, &key, 1, 1, 0, string_ref(), 0, 0);
}
if (cli->request_send() != 0) {
fprintf(stderr, "reuqest_send: %s\n", cli->get_error().c_str());
return;
}
size_t read_cnt = 0;
for (size_t j = 0; j < arg.sh.pipe; ++j) {
size_t num_flds = 0;
if (cli->response_recv(num_flds) != 0) {
fprintf(stderr, "reuqest_recv: %s\n", cli->get_error().c_str());
return;
}
{
++read_cnt;
++io_success_count;
arg.sh.increment_count();
{
++op_success_count;
}
}
cli->response_buf_remove();
}
if (!keep_connection) {
cli.reset();
}
}
}
void
hstest_thread::test_watch()
{
const int timelimit = arg.sh.conf.get_int("timelimit", 0);
const int timelimit_offset = timelimit / 2;
int loop = 0;
double t1 = 0, t2 = 0;
size_t cnt_t1 = 0, cnt_t2 = 0;
size_t prev_cnt = 0;
double now_f = 0;
while (true) {
sleep(1);
const size_t cnt = arg.sh.count;
const size_t df = cnt - prev_cnt;
prev_cnt = cnt;
const double now_prev = now_f;
now_f = gettimeofday_double();
if (now_prev != 0) {
const double rps = static_cast<double>(df) / (now_f - now_prev);
fprintf(stderr, "now: %zu cntdiff: %zu tdiff: %f rps: %f\n",
static_cast<size_t>(now_f), df, now_f - now_prev, rps);
}
if (timelimit != 0) {
if (arg.sh.wait_conn == 0 || arg.sh.conn_count >= arg.sh.wait_conn) {
++loop;
}
if (loop == timelimit_offset) {
t1 = gettimeofday_double();
cnt_t1 = cnt;
arg.sh.enable_timing = 1;
fprintf(stderr, "start timing\n");
} else if (loop == timelimit_offset + timelimit) {
t2 = gettimeofday_double();
cnt_t2 = cnt;
const size_t cnt_diff = cnt_t2 - cnt_t1;
const double tdiff = t2 - t1;
const double qps = cnt_diff / (tdiff != 0 ? tdiff : 1);
fprintf(stderr, "(%f: %zu, %f: %zu), %10.5f qps\n",
t1, cnt_t1, t2, cnt_t2, qps);
size_t keycnt = 0;
for (int i = 0; i < arg.sh.keygen_size; ++i) {
if (arg.sh.keygen[i]) {
++keycnt;
}
}
fprintf(stderr, "keygen=%zu\n", keycnt);
break;
}
}
}
#if 0
int loop = 0;
double t1 = 0, t2 = 0;
size_t cnt_t1 = 0, cnt_t2 = 0;
size_t prev_cnt = 0;
while (true) {
sleep(1);
const size_t cnt = arg.sh.count;
const size_t df = cnt - prev_cnt;
prev_cnt = cnt;
const size_t now = time(0);
fprintf(stderr, "%zu %zu\n", now, df);
if (timelimit != 0) {
++loop;
if (loop == timelimit_offset) {
t1 = gettimeofday_double();
cnt_t1 = cnt;
} else if (loop == timelimit_offset + timelimit) {
t2 = gettimeofday_double();
cnt_t2 = cnt;
const size_t cnt_diff = cnt_t2 - cnt_t1;
const double tdiff = t2 - t1;
const double qps = cnt_diff / (tdiff != 0 ? tdiff : 1);
fprintf(stderr, "(%f: %zu, %f: %zu), %10.5f qps\n",
t1, cnt_t1, t2, cnt_t2, qps);
size_t keycnt = 0;
for (int i = 0; i < arg.sh.keygen_size; ++i) {
if (arg.sh.keygen[i]) {
++keycnt;
}
}
fprintf(stderr, "keygen=%zu\n", keycnt);
_exit(0);
}
}
}
#endif
}
void
hstest_thread::test_12(int test_num)
{
/* NOTE: num_threads should be 1 */
/* create table hstest
* ( k varchar(255) not null, v varchar(255) not null, primary key(k))
* engine = innodb; */
mysqltest_thread_initobj initobj;
auto_mysql db;
std::string err;
unsigned long long err_cnt = 0;
unsigned long long query_cnt = 0;
#if 0
my_bool reconnect = 0;
if (mysql_options(db, MYSQL_OPT_RECONNECT, &reconnect) != 0) {
err = "mysql_options() failed";
++err_cnt;
return;
}
#endif
const std::string mysql_host = arg.sh.conf.get_str("host", "localhost");
const int mysql_port = arg.sh.conf.get_int("mysqlport", 3306);
const unsigned int num = arg.sh.loop;
const size_t pipe = arg.sh.pipe;
const std::string mysql_user = arg.sh.conf.get_str("mysqluser", "root");
const std::string mysql_passwd = arg.sh.conf.get_str("mysqlpass", "");
const std::string mysql_dbname = arg.sh.conf.get_str("db", "hstest");
const int keep_connection = arg.sh.conf.get_int("keep_connection", 1);
const int verbose = arg.sh.conf.get_int("verbose", 1);
const int use_handler = arg.sh.conf.get_int("handler", 0);
int connected = 0;
unsigned int k = 0;
string_buffer buf;
for (unsigned int i = 0; i < num; ++i) {
const int flags = 0;
if (connected == 0 && !mysql_real_connect(db, mysql_host.c_str(),
mysql_user.c_str(), mysql_user.empty() ? 0 : mysql_passwd.c_str(),
mysql_dbname.c_str(), mysql_port, 0, flags)) {
err = "failed to connect: " + std::string(mysql_error(db));
if (verbose >= 1) {
fprintf(stderr, "e=[%s]\n", err.c_str());
}
++err_cnt;
return;
}
int r = 0;
if (connected == 0 && use_handler) {
const char *const q = "handler hstest open";
r = mysql_real_query(db, q, strlen(q));
if (r != 0) {
err = 1;
}
}
connected = 1;
std::string result_str;
unsigned int err = 0;
unsigned int num_flds = 0, num_affected_rows = 0;
int got_data = 0;
buf.clear();
buf.append_literal("insert into hstest values ");
for (size_t j = 0; j < pipe; ++j) {
const unsigned int v = ~k;
if (j != 0) {
buf.append_literal(",");
}
char *wp = buf.make_space(64);
int buf_query_len = snprintf(wp, 64, "('k%u', 'v%u')", k, v);
buf.space_wrote(buf_query_len);
++k;
}
if (r == 0) {
r = mysql_real_query(db, buf.begin(), buf.size());
++query_cnt;
}
if (r != 0) {
err = 1;
} else {
auto_mysql_res res(db);
if (res != 0) {
if (verbose >= 0) {
num_flds = mysql_num_fields(res);
MYSQL_ROW row = 0;
while ((row = mysql_fetch_row(res)) != 0) {
got_data = 1;
unsigned long *const lengths = mysql_fetch_lengths(res);
if (verbose >= 2) {
for (unsigned int i = 0; i < num_flds; ++i) {
if (!result_str.empty()) {
result_str += " ";
}
result_str += std::string(row[i], lengths[i]);
}
}
}
}
} else {
if (mysql_field_count(db) == 0) {
num_affected_rows = mysql_affected_rows(db);
} else {
err = 1;
}
}
}
if (verbose >= 2 || (verbose >= 1 && err != 0)) {
if (err) {
++err_cnt;
const char *const errstr = mysql_error(db);
fprintf(stderr, "e=[%s] a=%u q=[%s]\n", errstr,
num_affected_rows, std::string(buf.begin(), buf.size()).c_str());
} else {
fprintf(stderr, "a=%u q=[%s] r=[%s]\n", num_affected_rows,
std::string(buf.begin(), buf.size()).c_str(),
result_str.c_str());
}
}
if (err == 0) {
++io_success_count;
if (num_affected_rows > 0 || got_data > 0) {
++op_success_count;
}
arg.sh.increment_count(pipe);
}
if (!keep_connection) {
db.reset();
connected = 0;
}
}
if (verbose >= 1) {
fprintf(stderr, "thread finished (error_count=%llu)\n", err_cnt);
}
}
void
hstest_thread::test_21(int num)
{
/* fsync test */
unsigned int id = arg.id;
std::string err;
#if 0
if (socket_connect(fd, arg.sh.arg, err) != 0) {
fprintf(stderr, "connect: %d %s\n", errno, strerror(errno));
return;
}
#endif
auto_file logfd;
char fname[1024];
snprintf(fname, sizeof(fname), "synctest_%u", id);
int open_flags = O_WRONLY | O_CREAT | O_TRUNC | O_APPEND;
logfd.reset(open(fname, open_flags, 0644));
if (logfd.get() < 0) {
fprintf(stderr, "open: %s: %d %s\n", fname, errno, strerror(errno));
return;
}
char buf[1024];
unsigned long long count = 0;
while (true) {
snprintf(buf, sizeof(buf), "%u %llu\n", id, count);
const size_t len = strlen(buf);
if (write(logfd.get(), buf, len) != (ssize_t)len) {
fprintf(stderr, "write: %s: %d %s\n", fname, errno, strerror(errno));
return;
}
#if 0
if (write(fd.get(), buf, len) != (ssize_t)len) {
fprintf(stderr, "write(sock): %d %s\n", errno, strerror(errno));
return;
}
#endif
if (fdatasync(logfd.get()) != 0) {
fprintf(stderr, "fsync: %s: %d %s\n", fname, errno, strerror(errno));
return;
}
++count;
++op_success_count;
arg.sh.increment_count();
}
}
void
hstest_thread::test_22(int num)
{
/* dd if=/dev/zero of=dummy.dat bs=1024M count=100 */
unsigned int id = arg.id;
std::string err;
auto_file filefd;
char fname[1024];
snprintf(fname, sizeof(fname), "dummy.dat");
int open_flags = O_RDONLY | O_DIRECT;
filefd.reset(open(fname, open_flags, 0644));
if (filefd.get() < 0) {
fprintf(stderr, "open: %s: %d %s\n", fname, errno, strerror(errno));
return;
}
char buf_x[4096 * 2];
char *const buf = (char *)(size_t(buf_x + 4096) / 4096 * 4096);
unsigned long long count = 0;
drand48_data randbuf;
unsigned long long seed = time(0);
seed *= 10;
seed += id;
srand48_r(seed, &randbuf);
for (unsigned int i = 0; i < arg.sh.loop; ++i) {
double kf = 0;
drand48_r(&randbuf, &kf);
kf *= (209715200 / 1);
// fprintf(stderr, "v=%f\n", kf);
off_t v = static_cast<off_t>(kf);
v %= (209715200 / 1);
v *= (512 * 1);
const double tm1 = gettimeofday_double();
const ssize_t r = pread(filefd.get(), buf, (512 * 1), v);
const double tm2 = gettimeofday_double();
if (r < 0) {
fprintf(stderr, "pread: %s: %d %s\n", fname, errno, strerror(errno));
return;
}
++count;
++op_success_count;
arg.sh.increment_count();
set_timing(tm2 - tm1);
}
}
void
hstest_thread::operator ()()
{
if (arg.watch_flag) {
return test_watch();
}
int test_num = arg.sh.conf.get_int("test", 1);
if (test_num == 1) {
test_1();
} else if (test_num == 2 || test_num == 3) {
test_2_3(test_num);
} else if (test_num == 4 || test_num == 5) {
test_4_5(test_num);
} else if (test_num == 6) {
test_6(test_num);
} else if (test_num == 7) {
test_7(test_num);
} else if (test_num == 8) {
test_8(test_num);
} else if (test_num == 9) {
test_9(test_num);
} else if (test_num == 10) {
test_10(test_num);
} else if (test_num == 11) {
test_11(test_num);
} else if (test_num == 12) {
test_12(test_num);
} else if (test_num == 21) {
test_21(test_num);
} else if (test_num == 22) {
test_22(test_num);
}
const int halt = arg.sh.conf.get_int("halt", 0);
if (halt) {
fprintf(stderr, "thread halted\n");
while (true) {
sleep(100000);
}
}
fprintf(stderr, "thread finished\n");
}
int
hstest_main(int argc, char **argv)
{
ignore_sigpipe();
hstest_shared shared;
parse_args(argc, argv, shared.conf);
shared.conf["port"] = shared.conf["hsport"];
shared.arg.set(shared.conf);
shared.loop = shared.conf.get_int("num", 1000);
shared.pipe = shared.conf.get_int("pipe", 1);
shared.verbose = shared.conf.get_int("verbose", 1);
const int tablesize = shared.conf.get_int("tablesize", 0);
std::vector<char> keygen(tablesize);
shared.keygen = &keygen[0];
shared.keygen_size = tablesize;
shared.usleep = shared.conf.get_int("usleep", 0);
shared.dump = shared.conf.get_int("dump", 0);
shared.num_threads = shared.conf.get_int("num_threads", 10);
shared.wait_conn = shared.conf.get_int("wait_conn", 0);
const std::string op = shared.conf.get_str("op", "G");
if (op.size() > 0) {
shared.op = op[0];
}
#if 0
const int localdb_flag = shared.conf.get_int("local", 0);
if (localdb_flag) {
shared.localdb = database_i::create(shared.conf);
}
#endif
const int num_thrs = shared.num_threads;
typedef thread<hstest_thread> thread_type;
typedef std::auto_ptr<thread_type> thread_ptr;
typedef auto_ptrcontainer< std::vector<thread_type *> > thrs_type;
thrs_type thrs;
for (int i = 0; i < num_thrs; ++i) {
const hstest_thread::arg_type arg(i, shared, false);
thread_ptr thr(new thread<hstest_thread>(arg));
thrs.push_back_ptr(thr);
}
for (size_t i = 0; i < thrs.size(); ++i) {
thrs[i]->start();
}
thread_ptr watch_thread;
const int timelimit = shared.conf.get_int("timelimit", 0);
{
const hstest_thread::arg_type arg(0, shared, true);
watch_thread = thread_ptr(new thread<hstest_thread>(arg));
watch_thread->start();
}
size_t iocnt = 0, opcnt = 0;
double respmin = 999999, respmax = 0;
double respsum = 0;
if (timelimit != 0) {
watch_thread->join();
}
for (size_t i = 0; i < thrs.size(); ++i) {
if (timelimit == 0) {
thrs[i]->join();
}
iocnt += (*thrs[i])->io_success_count;
opcnt += (*thrs[i])->op_success_count;
respmin = std::min(respmin, (*thrs[i])->response_min);
respmax = std::max(respmax, (*thrs[i])->response_max);
respsum += (*thrs[i])->response_sum;
}
fprintf(stderr, "io_success_count=%zu op_success_count=%zu\n", iocnt, opcnt);
fprintf(stderr, "respmin=%f respmax=%f respsum=%f respavg=%f\n",
respmin, respmax, respsum, respsum / opcnt);
size_t keycnt = 0;
for (size_t i = 0; i < keygen.size(); ++i) {
if (keygen[i]) {
++keycnt;
}
}
fprintf(stderr, "keycnt=%zu\n", keycnt);
_exit(0);
return 0;
}
};
int
main(int argc, char **argv)
{
return dena::hstest_main(argc, argv);
}
#!/usr/bin/perl
# vim:sw=8:ai:ts=8
use strict;
use warnings;
use DBI;
use Net::HandlerSocket;
my %conf = ();
for my $i (@ARGV) {
my ($k, $v) = split(/=/, $i);
$conf{$k} = $v;
}
my $verbose = get_conf("verbose", 0);
my $actions_str = get_conf("actions", "hsread");
my $tablesize = get_conf("tablesize", 10000);
my $db = get_conf("db", "hstest");
my $table = get_conf("table", "hstest_table1");
my $engine = get_conf("engine", "innodb");
my $host = get_conf("host", "localhost");
my $mysqlport = get_conf("mysqlport", 3306);
my $mysqluser = get_conf("mysqluser", "root");
my $mysqlpass = get_conf("mysqlpass", "");
my $hsport = get_conf("hsport", 9999);
my $loop = get_conf("loop", 10000);
my $op = get_conf("op", "=");
my $ssps = get_conf("ssps", 0);
my $num_moreflds = get_conf("moreflds", 0);
my $moreflds_prefix = get_conf("moreflds_prefix", "column0123456789_");
my $keytype = get_conf("keytype", "varchar(32)");
my $file = get_conf("file", undef);
my $dsn = "DBI:mysql:database=;host=$host;port=$mysqlport"
. ";mysql_server_prepare=$ssps";
my $dbh = DBI->connect($dsn, $mysqluser, $mysqlpass, { RaiseError => 1 });
my $hsargs = { 'host' => $host, 'port' => $hsport };
my $cli = new Net::HandlerSocket($hsargs);
my @actions = split(/,/, $actions_str);
for my $action (@actions) {
if ($action eq "table") {
print("TABLE $db.$table\n");
$dbh->do("drop database if exists $db");
$dbh->do("create database $db");
$dbh->do("use $db");
my $moreflds = get_createtbl_moreflds_str();
$dbh->do(
"create table $table (" .
"k $keytype primary key" .
",v varchar(32) not null" .
$moreflds .
") character set utf8 collate utf8_bin " .
"engine = $engine");
} elsif ($action eq "insert") {
print("INSERT $db.$table tablesize=$tablesize\n");
$dbh->do("use $db");
my $moreflds = get_insert_moreflds_str();
for (my $i = 0; $i < $tablesize; $i += 100) {
my $qstr = "insert into $db.$table values";
for (my $j = 0; $j < 100; ++$j) {
if ($j != 0) {
$qstr .= ",";
}
my $k = "" . ($i + $j);
my $v = "v" . int(rand(1000)) . ($i + $j);
$qstr .= "('$k', '$v'";
for (my $j = 0; $j < $num_moreflds; ++$j) {
$qstr .= ",'$j'";
}
$qstr .= ")";
}
$dbh->do($qstr);
print "$i/$tablesize\n" if $i % 1000 == 0;
}
} elsif ($action eq "read") {
print("READ $db.$table op=$op loop=$loop\n");
$dbh->do("use $db");
my $moreflds = get_select_moreflds_str();
my $sth = $dbh->prepare(
"select k,v$moreflds from $db.$table where k = ?");
for (my $i = 0; $i < $loop; ++$i) {
my $k = "" . int(rand($tablesize));
# print "k=$k\n";
$sth->execute($k);
if ($verbose >= 10) {
print "RET:";
while (my $ref = $sth->fetchrow_arrayref()) {
my $rk = $ref->[0];
my $rv = $ref->[1];
print " $rk $rv";
}
print "\n";
}
print "$i/$loop\n" if $i % 1000 == 0;
}
} elsif ($action eq "hsinsert") {
print("HSINSERT $db.$table tablesize=$tablesize\n");
$cli->open_index(1, $db, $table, '', 'k,v');
for (my $i = 0; $i < $tablesize; ++$i) {
my $k = "" . $i;
my $v = "v" . int(rand(1000)) . $i;
my $r = $cli->execute_insert(1, [ $k, $v ]);
if ($r->[0] != 0) {
die;
}
print "$i/$tablesize\n" if $i % 1000 == 0;
}
} elsif ($action eq "hsread") {
print("HSREAD $db.$table op=$op loop=$loop\n");
my $moreflds = get_select_moreflds_str();
$cli->open_index(1, $db, $table, '', "k,v$moreflds");
for (my $i = 0; $i < $loop; ++$i) {
my $k = "" . int(rand($tablesize));
# print "k=$k\n";
my $r = $cli->execute_find(1, $op, [ $k ], 1, 0);
if ($verbose >= 10) {
my $len = scalar(@{$r});
print "LEN=$len";
for my $e (@{$r}) {
print " [$e]";
}
print "\n";
}
print "$i/$loop\n" if $i % 1000 == 0;
}
} elsif ($action eq "hsupdate") {
my $vbase = "v" . int(rand(1000));
print("HSUPDATE $db.$table op=$op loop=$loop vbase=$vbase\n");
$cli->open_index(1, $db, $table, '', 'v');
for (my $i = 0; $i < $loop; ++$i) {
my $k = "" . int(rand($tablesize));
my $v = $vbase . $i;
print "k=$k v=$v\n";
my $r = $cli->execute_update(1, $op, [ $k ], 1, 0,
[ $v ]);
if ($verbose >= 10) {
print "UP k=$k v=$v\n";
}
print "$i/$loop\n" if $i % 1000 == 0;
}
} elsif ($action eq "hsdelete") {
print("HSDELETE $db.$table op=$op loop=$loop\n");
$cli->open_index(1, $db, $table, '', '');
for (my $i = 0; $i < $loop; ++$i) {
my $k = "" . int(rand($tablesize));
print "k=$k\n";
my $r = $cli->execute_delete(1, $op, [ $k ], 1, 0);
if ($verbose >= 10) {
print "DEL k=$k\n";
}
print "$i/$loop\n" if $i % 1000 == 0;
}
} elsif ($action eq "verify") {
verify_do();
}
}
sub verify_do {
my ($fail_cnt, $ok_cnt) = (0, 0);
my $sth = $dbh->prepare("select v from $db.$table where k = ?");
use FileHandle;
my $fh = new FileHandle($file, "r");
while (my $line = <$fh>) {
chomp($line);
my @vec = split(/\t/, $line);
my $k = $vec[3];
my $v = $vec[7];
next if (!defined($k) || !defined($v));
# print "$k $v\n";
$sth->execute($k);
my $aref = $sth->fetchrow_arrayref();
if (!defined($aref)) {
print "FAILED: $k notfound\n";
++$fail_cnt;
} else {
my $gv = $aref->[0];
if ($gv ne $v) {
print "FAILED: $k got=$gv expected=$v\n";
++$fail_cnt;
} else {
print "OK: $k $v $gv\n" if $verbose >= 10;
++$ok_cnt;
}
}
}
print "OK=$ok_cnt FAIL=$fail_cnt\n";
}
sub get_conf {
my ($key, $def) = @_;
my $val = $conf{$key};
if ($val) {
print "$key=$val\n";
} else {
$val = $def;
$def ||= '';
print "$key=$def(default)\n";
}
return $val;
}
sub get_createtbl_moreflds_str {
my $s = "";
for (my $j = 0; $j < $num_moreflds; ++$j) {
$s .= ",$moreflds_prefix$j varchar(30)";
}
return $s;
}
sub get_select_moreflds_str {
my $s = "";
for (my $i = 0; $i < $num_moreflds; ++$i) {
$s .= ",$moreflds_prefix$i";
}
return $s;
}
sub get_insert_moreflds_str {
my $s = "";
for (my $i = 0; $i < $num_moreflds; ++$i) {
$s .= ",?";
}
return $s;
}
#!/bin/bash
exec ./hstest test=10 tablesize=10000 host=localhost hsport=9998 num=10000000 \
num_threads=100 timelimit=10 $@
#!/bin/bash
exec ./hstest test=10 key_mask=9999 host=localhost port=9998 num=10000000 \
num_threads=100 timelimit=10 moreflds=50 $@
#!/bin/bash
./hstest test=7 key_mask=9999 host=localhost port=11211 num=10000 \
num_threads=10 timelimit=10 op=R $@
./hstest test=7 key_mask=9999 host=localhost port=11211 num=1000000 \
num_threads=100 timelimit=10 op=G $@
#!/bin/bash
exec ./hstest test=9 tablesize=9999 host=localhost mysqlport=3306 num=1000000 \
num_threads=100 verbose=1 timelimit=10 $@
#!/bin/bash
exec ./hstest test=9 key_mask=9999 host=localhost port=3306 num=1000000 \
num_threads=100 verbose=1 timelimit=10 moreflds=50 $@
# -*- Autoconf -*-
# Process this file with autoconf to produce a configure script.
#AC_PREREQ([2.63b])
AC_INIT([handlersocket-plugin], [1.0.6], [https://github.com/ahiguti/HandlerSocket-Plugin-for-MySQL/issues])
AC_CONFIG_HEADERS([config.h])
AM_INIT_AUTOMAKE([-Wall -Werror foreign])
AC_CONFIG_SRCDIR([libhsclient/fatal.cpp])
AC_CONFIG_MACRO_DIR([m4])
AC_PROG_CC
AC_PROG_CXX
AC_PROG_CPP
AC_PROG_LIBTOOL
AC_DEFUN([CONFIG_OPTION_MYSQL],[
AC_MSG_CHECKING([mysql source])
MYSQL_SOURCE_VERSION=
MYSQL_INC=
ac_mysql_source_dir=
AC_ARG_WITH([mysql-source],
[AS_HELP_STRING([--with-mysql-source=PATH], [MySQL source directory PATH])],
[
ac_mysql_source_dir=`cd $withval && pwd`
if test -f "$ac_mysql_source_dir/sql/handler.h" ; then
MYSQL_INC="-I$ac_mysql_source_dir/sql"
MYSQL_INC="$MYSQL_INC -I$ac_mysql_source_dir/include"
MYSQL_INC="$MYSQL_INC -I$ac_mysql_source_dir/regex"
MYSQL_INC="$MYSQL_INC -I$ac_mysql_source_dir"
AC_SUBST(MYSQL_INC)
if test -f "$ac_mysql_source_dir/VERSION"; then
source "$ac_mysql_source_dir/VERSION"
MYSQL_SOURCE_VERSION="$MYSQL_VERSION_MAJOR.$MYSQL_VERSION_MINOR.$MYSQL_VERSION_PATCH"
else
if test -f "$ac_mysql_source_dir/configure.in"; then
MYSQL_SOURCE_VERSION=`cat $ac_mysql_source_dir/configure.in | grep "\[[MySQL Server\]]" | sed -e "s|.*\([[0-9]]\+\.[[0-9]]\+\.[[0-9]]\+[[0-9a-zA-Z\_\-]]*\).*|\1|"`
else
AC_MSG_ERROR([invalid MySQL source directory: $ac_mysql_source_dir])
fi
fi
AC_MSG_RESULT([yes: Using $ac_mysql_source_dir, version $MYSQL_SOURCE_VERSION])
else
AC_MSG_ERROR([invalid MySQL source directory: $ac_mysql_source_dir])
fi
],
[AC_MSG_ERROR([--with-mysql-source=PATH is required for standalone build])]
)
MYSQL_BIN_VERSION=
ac_mysql_config=
AC_ARG_WITH([mysql-bindir],
[AS_HELP_STRING([--with-mysql-bindir=PATH], [MySQL binary directory PATH. This should be the directory where mysql_config is located.])],
[
mysql_bin_dir=`cd $withval 2> /dev/null && pwd || echo ""`
ac_mysql_config="$mysql_bin_dir/mysql_config"
],
[
AC_PATH_PROG([ac_mysql_config], [mysql_config])
]
)
AC_MSG_CHECKING([mysql binary])
if test ! -x "$ac_mysql_config" ; then
AC_MSG_ERROR([mysql_config not found! You have to specify the directory where mysql_config resides to --with-mysql-bindir=PATH.])
fi
MYSQL_CFLAGS_ADD=`"$ac_mysql_config" --cflags`
MYSQL_CFLAGS="$MYSQL_CFLAGS $MYSQL_CFLAGS_ADD -DFORCE_DBUG_OFF"
# FIXME
AC_SUBST(MYSQL_CFLAGS)
MYSQL_BIN_VERSION=`"$ac_mysql_config" --version`
AC_MSG_RESULT([yes: Using $ac_mysql_config, version $MYSQL_BIN_VERSION])
MYSQL_LIB=`"$ac_mysql_config" --libs_r`
LIB_DIR=`echo $MYSQL_LIB | sed -e "s|.*-L/|/|" | sed -e "s| .*||"`
# FIXME
if test a`basename "$LIB_DIR"` = amysql ; then
MYSQL_LIB="-L`dirname $LIB_DIR` $MYSQL_LIB"
# FIXME
fi
AC_SUBST(MYSQL_LIB)
if test a$MYSQL_SOURCE_VERSION != a$MYSQL_BIN_VERSION ; then
AC_MSG_ERROR([MySQL source version does not match MySQL binary version])
fi
AC_MSG_CHECKING([mysql plugin dir])
ac_mysql_plugin_dir=
AC_ARG_WITH([mysql-plugindir],
[AS_HELP_STRING([--with-mysql-plugindir=PATH], [MySQL plugin directory where handlersocket.so to be copied])],
[
ac_mysql_plugin_dir=`cd $withval && pwd`
if test -d "$ac_mysql_plugin_dir/" ; then
PLUGIN_DIR="$ac_mysql_plugin_dir"
AC_SUBST(PLUGIN_DIR)
AC_MSG_RESULT([yes: Using $ac_mysql_plugin_dir])
else
AC_MSG_ERROR([invalid MySQL plugin directory : $ac_mysql_plugin_dir])
fi
],
[
LIB_DIR_TMP=`"$ac_mysql_config" --plugindir`
if test ! -d "$LIB_DIR_TMP"; then
LIB_DIR_TMP=`"$ac_mysql_config" --libs_r | sed -e "s|.*-L/|/|" | sed -e "s| .*||"`/plugin
# FIXME
fi
ac_mysql_plugin_dir=$LIB_DIR_TMP
PLUGIN_DIR="$ac_mysql_plugin_dir"
AC_SUBST(PLUGIN_DIR)
AC_MSG_RESULT([--with-mysql-plugindir was not set. Using $ac_mysql_plugin_dir])
]
)
])
HANDLERSOCKET_SUBDIRS="libhsclient"
AC_ARG_ENABLE(handlersocket_server,
[ --enable-handlersocket-server build HandlerSocket plugin (defalut=yes)])
if test "$enable_handlersocket_server" != "no"; then
CONFIG_OPTION_MYSQL
HANDLERSOCKET_SUBDIRS="libhsclient handlersocket client"
fi
AC_SUBST(HANDLERSOCKET_SUBDIRS)
CFLAGS="$CFLAGS -Werror"
CXXFLAGS="$CXXFLAGS -Wall -g -fno-rtti -fno-exceptions -fPIC -DPIC"
AC_CONFIG_FILES([Makefile
handlersocket/Makefile
libhsclient/Makefile
client/Makefile])
AC_OUTPUT
-----------------------------------------------------------------------------
HandlerSocket plugin for MySQL
Copyright (c) 2010 DeNA Co.,Ltd.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of DeNA Co.,Ltd. nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY DeNA Co.,Ltd. "AS IS" AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
EVENT SHALL DeNA Co.,Ltd. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-----------------------------------------------------------------------------
About HandlerSocket
HandlerSocket is a NoSQL plugin for MySQL. It works as a daemon inside the
mysqld process, accept tcp connections, and execute requests from clients.
HandlerSocket does not support SQL queries. Instead, it supports simple CRUD
operations on tables.
Because of the following reasons, HandlerSocket is much faster than the
mysqld/libmysql pair in some circumstances:
- HandlerSocket manipulates data without parsing SQL, which causes less
CPU usage.
- HandlerSocket reads many requests from clients and executes their
requests in bulk, which causes less CPU and disk usage.
- HandlerSocket client/server protocol is more compact than the
mysql/libmysql pair, which causes less network usage.
The current version of HandlerSocket only works with GNU/Linux. The source
archive of HandlerSocket includes a C++ and a Perl client libraries.
Here is a list of other language bindings:
- PHP
http://openpear.org/package/Net_HandlerSocket
http://github.com/tz-lom/HSPHP
http://code.google.com/p/php-handlersocket/
- Java
http://code.google.com/p/handlersocketforjava/
- Python
https://code.launchpad.net/~songofacandy/+junk/pyhandlersocket
- Ruby
https://github.com/winebarrel/ruby-handlersocket
https://github.com/miyucy/handlersocket
- JavaScript(Node.js)
https://github.com/koichik/node-handlersocket
The home of HandlerSocket is here:
https://github.com/ahiguti/HandlerSocket-Plugin-for-MySQL
More documents are available in docs-en/ and docs-ja/ directories.
-----------------------------------------------------------------
handlersocket_verbose (default = 10, min = 0, max = 10000)
Specify the logging verboseness.
-----------------------------------------------------------------
handlersocket_address (default = '')
Specify the address to bind. If empty, it binds to 0.0.0.0.
-----------------------------------------------------------------
handlersocket_port (default = '9998')
Specify the port to bind. This option is for the listener for
read requests. If empty, the listener is disabled.
-----------------------------------------------------------------
handlersocket_port_wr (default = '9999')
Specify the port to bind. This option is for the listener for
write requests. If empty, the listener is disabled.
-----------------------------------------------------------------
handlersocket_epoll (default = 1, min = 0, max = 1)
Specify whether handlersocket uses epoll for I/O multiplexing.
-----------------------------------------------------------------
handlersocket_threads (default = 16, min = 1, max = 3000)
Specify the number of handlersocket worker threads. This option
is for the listener for read requests. Recommended value is
(the number of CPU cores * 2).
-----------------------------------------------------------------
handlersocket_threads_wr (default = 1, min = 1, max = 3000)
Specify the number of handlersocket worker threads. This option
is for the listener for write requests. Recommended value is 1.
-----------------------------------------------------------------
handlersocket_timeout (default = 300, min = 30, max = 3600)
Specify the socket timeout in seconds.
-----------------------------------------------------------------
handlersocket_backlog (default = 32768, min = 5, max = 1000000)
Specify the length of the listen backlog.
-----------------------------------------------------------------
handlersocket_sndbuf (default = 0, min = 0, max = 1677216)
Specify the maximum socket send buffer in bytes. If 0, the
system-wide default value is set.
-----------------------------------------------------------------
handlersocket_rcvbuf (default = 0, min = 0, max = 1677216)
Specify the maximum socket receive buffer in bytes. If 0, the
system-wide default value is set.
-----------------------------------------------------------------
handlersocket_readsize (default = 0, min = 0, max = 1677216)
Specify the minimum length of the handlersocket request buffer.
Larger value can make handlersocket faster for large requests,
but can consume memory. The default value is possibly 4096.
-----------------------------------------------------------------
handlersocket_accept_balance (default = 0, min = 0, max = 10000)
When this option is set to non-zero, handlersocket tries to
balance accepted connections among threads. Non-zero is
recommended if you use persistent connections (i.e., connection
pooling on the client side).
-----------------------------------------------------------------
handlersocket_wrlock_timeout (default = 12, min = 0, max = 3600)
Specify the lock timeout in seconds. When a write request is
performed, handlersocket locks an advisory lock named
'handlersocket_wr'. This option sets the timeout for the
locking.
1. Building Handlersocket
Handlersocket mainly consists of libhsclient, handlersocket, and C++/Perl clients. libhsclient is a common library shared from both client and server(plugin). handlersocket is a MySQL daemon plugin.
To build Handlersocket, you need both MySQL source code and MySQL binary. It is not required to pre-build MySQL source code, but source itself is needed because Handlersocket depends on MySQL header files that only MySQL source distribution contains. MySQL binary is just a normal MySQL binary distribution. You can use official MySQL binaries provided by Oracle.
Since Handlersocket uses daemon plugin interface supported from MySQL 5.1,
MySQL 5.1 or higher version is required.
Please make sure that you use identical MySQL version between MySQL source
and MySQL binary. Otherwise you might encounter serious problems (i.e. server
crash, etc).
Here are steps to build Handlersocket.
* Get MySQL source code
* Get MySQL binary
* Build Handlersocket
$ ./autogen.sh
$ ./configure --with-mysql-source=/work/mysql-5.1.50 --with-mysql-bindir=/work/mysql-5.1.50-linux-x86_64-glibc23/bin --with-mysql-plugindir=/work/mysql-5.1.50-linux-x86_64-glibc23/lib/plugin
--with-mysql-source refers to the top of MySQL source directory,
--with-mysql-bindir refers to where MySQL binary executables (i.e.
mysql_config) are located, and --with-mysql-plugindir refers to a plugin
directory where plugin libraries (*.so) are installed.
$ make
$ sudo make install
Both libhsclient and the handlersocket plugin will be installed.
2. Using Handlersocket
Append configuration options for handlersocket to my.cnf.
[mysqld]
loose_handlersocket_port = 9998
# the port number to bind to (for read requests)
loose_handlersocket_port_wr = 9999
# the port number to bind to (for write requests)
loose_handlersocket_threads = 16
# the number of worker threads (for read requests)
loose_handlersocket_threads_wr = 1
# the number of worker threads (for write requests)
open_files_limit = 65535
# to allow handlersocket accept many concurrent
# connections, make open_files_limit as large as
# possible.
Log in to mysql as root, and execute the following query.
mysql> install plugin handlersocket soname 'handlersocket.so';
If handlersocket.so is successfully installed, it starts
accepting connections on port 9998 and 9999. Running
'show processlist' should show handlersocket worker threads.
-----------------------------------------------------------------
On the client side, you need to install libhsclient for c++ apps
and perl-Net-HandlerSocket for perl apps. They do not require
MySQL to compile.
$ ./autogen.sh
$ ./configure --disable-handlersocket-server
$ make
$ sudo make install
$ cd perl-Net-HandlerSocket
$ perl Makefile.PL
$ make
$ sudo make install
-----------------------------------------------------------------
Alternatively, you can use the rpm installation. If your OS
supports rpms, you can use the following commands to build and
install handlersocket rpm packages.
(Server side, installs HandlerSocket plugin)
$ ./autogen.sh
$ ./configure --with-mysql-source=/work/mysql-5.1.50 --with-mysql-bindir=/work/mysql-5.1.50-linux-x86_64-glibc23/bin --with-mysql-plugindir=/work/mysql-5.1.50-linux-x86_64-glibc23/lib/plugin
$ make rpm_cli
$ sudo rpm -U dist/RPMS/*/libhsclient*.rpm
$ make rpm_c
$ sudo rpm -U dist/RPMS/*/handlersocket*.rpm
(Client side, installs client libraries)
$ ./autogen.sh
$ ./configure --disable-handlersocket-server
$ make rpm_cli
$ sudo rpm -U dist/RPMS/*/libhsclient*.rpm
$ make rpm_perl
$ sudo rpm -U dist/RPMS/*/perl-Net-HandlerSocket*.rpm
-----------------------------------------------------------------
To open a connection to the handlersocket plugin, you need to
create a Net::HandlerSocket object.
use Net::HandlerSocket;
my $args = { host => 'localhost', port => 9998 };
my $hs = new Net::HandlerSocket($args);
-----------------------------------------------------------------
Before executing table operations, you need to open an index to
work with.
my $err = $hs->open_index(3, 'database1', 'table1', 'PRIMARY',
'f1,f2');
die $hs->get_error() if $res->[0] != 0;
The first argument for open_index is an integer value which is
used to identify an open table, which is only valid within the
same Net::HandlerSocket object. The 4th argument is the name of
index to open. If 'PRIMARY' is specified, the primary index is
open. The 5th argument is a comma-separated list of column names.
-----------------------------------------------------------------
To read a record from a table using an index, call the
execute_single method.
my $res = $hs->execute_single(3, '=', [ 'foo' ], 1, 0);
die $hs->get_error() if $res->[0] != 0;
shift(@$res);
The first argument must be an integer which has specified as the
first argument for open_index on the same Net::HandlerSocket
object. The second argument specifies the search operation. The
current version of handlersocket supports '=', '>=', '<=', '>',
and '<'. The 3rd argument specifies the key to find, which must
an arrayref whose length is equal to or smaller than the number
of key columns of the index. The 4th and the 5th arguments
specify the maximum number of records to be retrieved, and the
number of records skipped before retrieving records. The columns
to be retrieved are specified by the 5th argument for the
corresponding open_index call.
The execute_single method always returns an arrayref. The first
element is the error code, which is 0 when no error is occured.
The remaining are the field values. If more than one record is
returned, it is flatten to an 1-dimensional array. For example,
when 5 records that have 3 columns are returned, you can retrieve
values using the following code.
die $hs->get_error() if $res->[0] != 0;
shift(@$res);
for (my $row = 0; $row < 5; ++$row) {
for (my $col = 0; $col < 3; ++$col) {
my $value = $res->[$row * 5 + $col];
# ...
}
}
-----------------------------------------------------------------
To update or delete records, you need to specify more arguments
for the execute_single method. Note that the Net::HandlerSocket
object must be connected to a handlersocket worker for write
operations, which is port 9999 by default.
(For safety, the port 9998 only allows read operations, and the
port 9999 allows write operations also. The port 9999 allows
read operations too, but slower than 9998 because of record
locking etc.. Port numbers can be changed using the
'handlersocket_port' and the 'handlersocket_port_wr'
configuration options of mysqld.)
my $args = { host => 'localhost', port => 9999 };
my $hs = new Net::HandlerSocket($args);
my $res = $hs->execute_single(3, '=', [ 'bar' ], 1, 0, 'U',
[ 'fubar', 'hoge' ]);
die $hs->get_error() if $res->[0] != 0;
my $num_updated_rows = $res->[1];
my $res = $hs->execute_single(3, '=', [ 'baz' ], 1, 0, 'D');
die $hs->get_error() if $res->[0] != 0;
my $num_deleted_rows = $res->[1];
The 6th argument for execute_single specifies the modification
operation. The current version supports 'U' and 'D'. For the 'U'
operation, the 7th argument specifies the new value for the row.
The columns to be modified are specified by the 5th argument for
the corresponding open_index call. For the 'D' operation, the
7th argument can be omitted.
-----------------------------------------------------------------
The execute_single method can be used for inserting records also.
my $res = $hs->execute_single(3, '+', [ 'foo', 'bar', 'baz' ]);
die $hs->get_error() if $res->[0] != 0;
my $num_inserted_rows = $res->[1];
The 3rd argument must be an arrayref whose elements correspond to
the 5th argument for the corresponding open_index call. If there
is a column which is not appeared in the 5th argument for the
open_index, the default value for the column is set.
-----------------------------------------------------------------
Multiple operations can be executed in a single call. Executing
multiple operations in a single call is much faster than
executing them separatedly.
my $rarr = $hs->execute_multi([
[ 0, '>=', [ 'foo' ], 5, 0 ],
[ 2, '=', [ 'bar' ], 1, 0 ],
[ 4, '<', [ 'baz' ], 10, 5 ],
]);
for my $res (@$rarr) {
die $hs->get_error() if $res->[0] != 0;
shift(@$res);
# ...
}
-----------------------------------------------------------------
When an error is occured, the first element of the returned
arrayref becomes a non-zero value. A negative value indicates
that an I/O error is occured and the Net::HandlerSocket object
should be disposed. A positive value means that the connection is
still active and the Net::HandlerSocket object can be reused
later.
----------------------------------------------------------------------------
The HandlerSocket protocol
----------------------------------------------------------------------------
Basic syntax
- The HandlerSocket protocol is line-based. Each line ends with LF(0x0a).
- Each line consists a concatenation of tokens separated by HT(0x09).
- A token is either NULL or an encoded string. Note that you need to
distinguish NULL from an empty string, as most DBMs does so.
- NULL is expressed as a single NUL(0x00).
- An encoded string is a string with the following encoding rules.
- Characters in the range [0x10 - 0xff] are not encoded.
- A character in the range [0x00 - 0x0f] is prefixed by 0x01 and
shifted by 0x40. For example, 0x03 is encoded as 0x01 0x43.
- Note that a string can be empty. A continuation of 0x09 0x09 means that
there is an empty string between them. A continuation of 0x09 0x0a means
that there is an empty string at the end of the line.
----------------------------------------------------------------------------
Request and Response
- The HandlerSocket protocol is a simple request/response protocol. After a
connection is established, the client side sends a request, and then the
server side sends a response.
- A request/response consists of a single line.
- Requests can be pipelined; That is, you can send multiple requests (ie.
lines) at one time, and receive responses for them at one time.
----------------------------------------------------------------------------
'open_index' request
The 'open_index' request has the following syntax.
P <indexid> <dbname> <tablename> <indexname> <columns>
- <indexid> is a number in decimal.
- <dbname>, <tablename>, and <indexname> are strings. To open the primary
key, use PRIMARY as <indexname>.
- <columns> is a comma-separated list of column names.
Once an 'open_index' request is issued, the HandlerSocket plugin opens the
specified index and keep it open until the client connection is closed. Each
open index is identified by <indexid>. If <indexid> is already open, the old
open index is closed. You can open the same combination of <dbname>
<tablename> <indexname> multple times, possibly with different <columns>.
For efficiency, keep <indexid> small as far as possible.
----------------------------------------------------------------------------
Getting data
The 'find' request has the following syntax.
<indexid> <op> <vlen> <v1> ... <vn> <limit> <offset>
- <indexid> is a number. This number must be an <indexid> specified by a
'open_index' request executed previously on the same connection.
- <op> specifies the comparison operation to use. The current version of
HandlerSocket supports '=', '>', '>=', '<', and '<='.
- <vlen> indicates the length of the trailing parameters <v1> ... <vn>. This
must be smaller than or equal to the number of index columns specified by
specified by the corresponding 'open_index' request.
- <v1> ... <vn> specify the index column values to fetch.
- <limit> and <offset> are numbers. These parameters can be omitted. When
omitted, it works as if 1 and 0 are specified.
----------------------------------------------------------------------------
Updating/Deleting data
The 'find_modify' request has the following syntax.
<indexid> <op> <vlen> <v1> ... <vn> <limit> <offset> <mop> <m1> ... <mk>
- <mop> is either 'U' (update) or 'D' (delete).
- <m1> ... <mk> specifies the column values to set. The length of <m1> ...
<mk> must be smaller than or equal to the length of <columns> specified by
the corresponding 'open_index' request. If <mop> is 'D', these parameters
are ignored.
----------------------------------------------------------------------------
Inserting data
The 'insert' request has the following syntax.
<indexid> '+' <vlen> <v1> ... <vn>
- <vlen> indicates the length of the trailing parameters <v1> ... <vn>. This
must be smaller than or equal to the length of <columns> specified by the
corresponding 'open_index' request.
- <v1> ... <vn> specify the column values to set. For columns not in
<columns>, the default values for each column are set.
----------------------------------------------------------------------------
Response syntax
HandlerSocket returns a response of the following syntax for each request.
<errorcode> <numcolumns> <r1> ... <rn>
- <errorcode> indicates whether the request has successfully executed or not.
'0' means success. Non-zero means an error.
- <numcolumns> indicates the number of columns of the result set.
- <r1> ... <rn> is the result set. The length of <r1> ... <rn> is always a
multiple of <numcolumns>. It is possible that <r1> ... <rn> is empty.
If <errorcode> is non-zero, <numcolumns> is always 1 and <r1> indicates a
human-readable error message, though sometimes <r1> is not provided.
----------------------------------------------------------------------------
Response for 'open_index'
If 'open_index' is succeeded, HandlerSocket returns a line of the following
syntax.
0 1
----------------------------------------------------------------------------
Response for 'find'
If 'find' is succeeded, HandlerSocket returns a line of the following
syntax.
0 <numcolumns> <r1> ... <rn>
- <numcolumns> always equals to the length of <columns> of the corresponding
'open_index' request.
- <r1> ... <rn> is the result set. If N rows are found, the length of <r1>
... <rn> becomes ( <numcolumns> * N ).
----------------------------------------------------------------------------
Response for 'find_modify'
If 'find_modify' is succeeded, HandlerSocket returns a line of the following
syntax.
0 1 <nummod>
- <nummod> is the number of modified rows.
----------------------------------------------------------------------------
Response for 'insert'
If 'insert' is succeeded, HanderSocket returns a line of the following
syntax.
0 1
-----------------------------------------------------------------
ソースコードの利用にあたっての免責事項
本ソフトウェアの開発者および株式会社ディー・エヌ・エーは、本フト
ウェアの不稼動、稼動不良を含む法律上の瑕疵担保責任、その他保証責
任を負わないものとします。また、本ソフトウエアの開発者および株式
会社ディー・エヌ・エーは、本ソフトウェアの商品性、またはお客様の
特定の目的に対する適合性について、いかなる保証も負わないものとし
ます。
-----------------------------------------------------------------
handlersocket pluginについて
mysqlサーバに常駐し、innodb等のストレージエンジンへの直接のアクセ
スを提供するプラグインです。handlersocketプラグインは自前のリス
ナーを持ち、専用のクライアントライブラリ(libhsclient)を使ってそれ
にアクセスします。
mysqlの標準クライアントライブラリ(libmysql)を使ったアクセスと比べ
て、以下のような利点があります。
・接続あたりに消費するリソースが少ないため、同時接続数が事実上無
制限。したがって接続数を気にせず持続接続を使えます。
・高速(単純な参照クエリで3倍〜10倍程度)。
・通信プロトコルがコンパクト。libmysqlを使うとデータ転送時にレ
コード名などが付随するために通信内容が冗長ですが、libhsclientで
はデータのみが転送されるため、帯域消費が少なくなります。場合に
よっては10倍以上libmysqlのほうが冗長になります。
現在のバージョンでは以下のような処理をサポートしています。
・指定された索引について、指定された値と完全一致するようなレコー
ドを取得。(SELECT ??? FROM tbl WHERE k1 = v1 AND k2 = v2...)。
索引を使わない検索はサポートしていません。
・指定された索引について、指定された値の位置の前後のレコードを取
得。(SELECT ??? FROM tbl WHERE k1 >= v1 LIMIT 100)
・前述のような手段で取得したレコードに対するUPDATEとDELETE
・レコードのINSERT
以下のような言語をサポートします。
・C++。libhsclientをリンクします。
・Perl。Net::HandlerSocketをuseします。
現在のバージョンではGNU/Linuxでのみ動作します。
-----------------------------------------------------------------
既知の問題
・killでhandlersocketスレッドを殺すと、スレッド数が減ったまま回復
しません。
-----------------------------------------------------------------
HandlerSocketプラグインのビルド方法(RPMを使わない方法)
以下のようにしてconfigureを実行します。
$ ./autogen.sh
$ ./configure --with-mysql-source=/work/mysql-5.1.50 --with-mysql-bindir=/work/mysql-5.1.50-linux-x86_64-glibc23/bin --with-mysql-plugindir=/work/mysql-5.1.50-linux-x86_64-glibc23/lib/plugin
ここで--with-mysql-sourceにはMySQLのソースコードのトップディレク
トリを指定します。--with-mysql-bindirにはインストール済みのMySQL
のmysql_configコマンドが有るディレクトリを指定します。
その後以下のようにビルド・インストールします。
$ make
$ sudo make install
-----------------------------------------------------------------
クライアントライブラリのビルド方法(RPMを使わない方法)
クライアントライブラリをビルドする際には、MySQLのソースコードは
必要ありません。またMySQLがインストールされている必要もありません。
$ ./autogen.sh
$ ./configure --disable-handlersocket-server
$ make
$ sudo make install
$ cd perl-Net-HandlerSocket
$ perl Makefile.PL
$ make
$ sudo make install
-----------------------------------------------------------------
ビルド方法(RPM)
以下のように実行すれば、rpmパッケージがビルド&インストールされま
す。
(MySQLサーバ側、HandlerSocketプラグインをインストールする)
$ ./autogen.sh
$ ./configure --with-mysql-source=/work/mysql-5.1.50 --with-mysql-bindir=/work/mysql-5.1.50-linux-x86_64-glibc23/bin --with-mysql-plugindir=/work/mysql-5.1.50-linux-x86_64-glibc23/lib/plugin
$ make rpm_cli
$ sudo rpm -U dist/RPMS/*/libhsclient*.rpm
$ make rpm_c
$ sudo rpm -U dist/RPMS/*/handlersocket*.rpm
(クライアント側、クライアントライブラリをインストールする)
$ ./autogen.sh
$ ./configure --disable-handlersocket-server
$ make rpm_cli
$ sudo rpm -U dist/RPMS/*/libhsclient*.rpm
$ make rpm_perl
$ sudo rpm -U dist/RPMS/*/perl-Net-HandlerSocket*.rpm
-----------------------------------------------------------------
起動
mysqlを起動した状態で、mysqlの設定ファイル(my.cnf等)に以下の内容を
追加します。
[mysqld]
handlersocket_port = 9998
# handlersocketが接続を受け付けるポート(参照系リクエスト用)
handlersocket_port_wr = 9999
# handlersocketが接続を受け付けるポート(更新系リクエスト用)
handlersocket_address =
# handlersocketがバインドするアドレス(空のままでOK)
handlersocket_verbose = 0
# デバッグ用
handlersocket_timeout = 300
# 通信タイムアウト(秒)
handlersocket_threads = 16
# handlersocketのワーカースレッド数
thread_concurrency = 128
# handlersocketが幾つかのスレッドを占有するため、大きめの
# 値を指定してください
open_files_limit = 65535
# ソケットを大量に開けるようにするため、大きめの値を指定し
# てください
以下のクエリを実行します。
mysql> install plugin handlersocket soname 'handlersocket.so';
Query OK, 0 rows affected (0.06 sec)
以上でhandlersocketへクライアントからアクセスできるようになります。
-----------------------------------------------------------------
handlersocketプラグインへの接続を開くには、Net::HandlerSocketオブ
ジェクトを作成します。
use Net::HandlerSocket;
my $args = { host => 'localhost', port => 9998 };
my $hs = new Net::HandlerSocket($args);
-----------------------------------------------------------------
検索などの命令を実行する前に、処理対象となる索引を開く必要があり
ます。
my $err = $hs->open_index(3, 'database1', 'table1', 'PRIMARY',
'f1,f2');
die $hs->get_error() if $res->[0] != 0;
最初の引数は開く索引に付ける番号です。付けた番号は同一の
Net::HandlerSocketオブジェクトについてのみ有効です。第4引数は開く
索引の名前で、「PRIMARY」が指定され場合はプライマリキーが開かれま
す。第5引数は「,」で区切られた列名のリストです。
-----------------------------------------------------------------
テーブルから索引を使って行を取得するには、execute_singleメソッド
を呼びます。
my $res = $hs->execute_single(3, '=', [ 'foo' ], 1, 0);
die $hs->get_error() if $res->[0] != 0;
shift(@$res);
最初の引数は索引の番号で、同じNet::HandlerSocketオブジェクトへ
open_indexで付けたものでなければなりません。第2引数には操作を指定
します。現在のバージョンでは、「=」、「>=」、「<=」、「>」、「<」
の操作が利用可能です。第3引数は配列への参照で、これは探すべき行の
キー値を指定します。配列の長さは索引のキーの個数と同じか少ない数
でなければなりません。第4引数と第5引数はそれぞれ、取得する最大行
数、取得前に読み飛ばす行数を指定します。取得される列は対応する
open_index呼び出しの第5引数で指定されたものになります。
execute_singleメソッドは常に配列への参照を返します。最初の要素は
エラーコードで、これが0ならば成功を表します。残りの要素は列の値で
す。もし取得されたデータが複数行の場合は、それが一つの配列へ連結
された形で格納されています。例えば、5行3列のデータの場合、次のよ
うなコードによってその内容を取得できます。
die $hs->get_error() if $res->[0] != 0;
shift(@$res);
for (my $row = 0; $row < 5; ++$row) {
for (my $col = 0; $col < 3; ++$col) {
my $value = $res->[$row * 5 + $col];
# ...
}
}
-----------------------------------------------------------------
行を更新または削除するには、更に多くの引数を指定して
execute_singleメソッドを呼び出します。書き込み処理をするには、
対象のNet::HandlerSocketオブジェクトは更新用handlersocketワーカ(既
定ではポート9999)へ接続されたものでなくてはなりません。
(安全のため、ポート9998は参照処理だけを受け付け、ポート9999は更新
処理も受け付けるようになっています。ポート9999は参照処理も受け付
けますが、レコードロック等の影響で遅くなります。ポート番号は
mysqldの「handlersocket_port」と「handlersocket_port_wr」の設定項
目で変更できます。)
my $args = { host => 'localhost', port => 9999 };
my $hs = new Net::HandlerSocket($args);
my $res = $hs->execute_single(3, '=', [ 'bar' ], 1, 0, 'U',
[ 'fubar', 'hoge' ]);
die $hs->get_error() if $res->[0] != 0;
my $num_updated_rows = $res->[1];
my $res = $hs->execute_single(3, '=', [ 'baz' ], 1, 0, 'D');
die $hs->get_error() if $res->[0] != 0;
my $num_deleted_rows = $res->[1];
execute_singleの第6引数は変更処理の種類を指定します。現在のバー
ジョンでは「U」と「D」が利用可能です。「U」については、第7引数で
新しい値を指定します。更新される列は、対応するopen_index呼び出し
の第5引数で指定されたものになります。「D」については第7引数は省
略できます。
-----------------------------------------------------------------
execute_singleメソッドは列の挿入にも使用できます。
my $res = $hs->execute_single(3, '+', [ 'foo', 'bar', 'baz' ]);
die $hs->get_error() if $res->[0] != 0;
my $num_inserted_rows = $res->[1];
第3引数は、対応するopen_index呼び出しの第5引数の列リストと同じだ
けの長さの配列への参照でなければなりません。open_index呼び出しの
第5引数に指定されていない列については、その列の既定値がセットされ
ます。
-----------------------------------------------------------------
execute_multiメソッドを使えば、複数のリクエストを一つの呼び出しで
実行することができます。これはリクエストを個別に実行するより高速
です。
my $rarr = $hs->execute_multi([
[ 0, '>=', [ 'foo' ], 5, 0 ],
[ 2, '=', [ 'bar' ], 1, 0 ],
[ 4, '<', [ 'baz' ], 10, 5 ],
]);
for my $res (@$rarr) {
die $hs->get_error() if $res->[0] != 0;
shift(@$res);
# ...
}
-----------------------------------------------------------------
エラーが起こると返値の配列参照の最初の要素が0以外になります。負の
数の場合はI/Oエラーが起こったことを示し、その場合はその
Net::HandlerSocketオブジェクトは破棄するべきです。正の値の場合は
接続は維持されているため、そのオブジェクトはそれ以後も再利用でき
ます。
-----------------------------------------------------------------
handlersocketの通信プロトコル
-----------------------------------------------------------------
構文
・コマンド行は改行(LF)で終わる。
・コマンド行は複数のトークンからなり、トークン間はTABで区切られる。
・トークンはNULLトークンか、文字列トークンのいずれか。
・NULLトークンは単一のNUL文字であらわされる。
・文字列トークンは、0バイト以上の文字列であらわされる。ただし0x10
未満の文字については0x01を前置し、0x40を加えたコードであらわさ
れる。それ以外の文字はその文字自身のコードであらわされる。
-----------------------------------------------------------------
リクエストとレスポンス
・接続が確立した直後の状態では、まずクライアントがコマンド行を送
る。(リクエスト)
・サーバはクライアントが送ったリクエストと丁度同じ数のコマンド行
を返す。(レスポンス)
・リクエストはパイプライン化してよい。つまりクライアントは前に
送ったリクエストに対する返事を待たずに次のリクエストを送っても
よい。
-----------------------------------------------------------------
リクエスト
・open_index命令は次のような構文を持つ。
'P' indexid dbname tablename indexname fieldlist
indexidは開いている索引に付けられる番号で、同一接続上で後に実行
する命令の、対象索引を指定するために使われる。dbname、tablename、
indexnameはそれぞれ開きたいDB、テーブル、索引の名前。索引の名前
として"PRIMARY"を指定するとプライマリキーが開かれる。fieldlist
はカンマ区切りの列名のリスト。
・find命令は次のような構文を持つ。
indexid op nflds v1 ... vn limit offset
indexidは実行対象の索引を指定する。opは索引検索の演算子(後述)。
v1からvnは可変長で、その個数はnflds。nfldsはindexidで指定された
open_index命令のindexnameの索引のfieldlistのフィールド数に等し
いか小さくなくてはならない。m2からmkは可変長で、その個数は
indexidで指定されたopen_index命令が発行された際のfieldlistに一
致しなければならない。コマンド行のlimit以降は省略できる。limit
とoffsetは、検索条件に合致する列のうちレスポンスに返す列数の上
限と、スキップする列数。limitとoffsetを省略した場合はそれぞれ1
と0が指定されたときと同じ動作をする。find命令はレスポンスとして、
条件に合致した列のリストを返す。opとして指定できる演算子は次の
とおり。
'=' - v1 ... vnと一致するものを取得
'>' - v1 ... vnより大きいものを昇順に取得
'>=' - v1 ... vnに一致するか大きいものを昇順に取得
'<' - v1 ... vnより小さいものを降順に取得
'<=' - v1 ... vnに一致するか等しいものを降順に取得
nfldsが1より大きい(v1 ... vnが2個以上)ときは辞書式順序で比較さ
れる。
・find_modify命令は次のような構文を持つ。
indexid op nflds v1 ... vn limit offset modop m1 ... mk
modopより前の部分はfind命令と同等で、これによって操作対象の行を
指定する。その操作対象の行に対しmodopで指定された変更処理を実行
する。m1 ... mkは可変長で、省略できる。modopは次いずれか。
'U' - indexidで指定されたopen_index命令のfieldlist列
の内容を、m1 ... mkの値で更新する。
'D' - 対象の行を削除する。m1 ... mkの値は無視される。
・insert命令はのような構文を持つ。
indexid '+' nflds v1 ... vn
indexidで指定されたテーブルに、列を挿入する。v1 ... vnは可変長
で、その個数はnflds。nfldsはindexidで指定されたopen_index命令の
indexnameの索引のfieldlistのフィールド数に等しいか小さくなくて
はならない。
-----------------------------------------------------------------
レスポンス
・open_index命令が成功したとき、レスポンスは次の構文を持つ。
'0' '1'
・find命令が成功したとき、レスポンスは次の構文を持つ。
'0' nflds v1 ... vn
nfldsは結果セットの列の数をあらわす。v1 ... vnは可変長で、その
長さはnfldsの整数倍。v1 ... vnは空のこともあり、それは条件に合
致するレコードが存在しなかったことをあらわす。結果セットが複数
行になったときはv1 ... vnの長さがnfldsの2倍以上となり、最初の
行から順にv1 ... vnにセットされる。
・modify命令が成功したとき、レスポンスは次の構文を持つ。
'0' '1' nummod
nummodは変更が施された行数。nummodが0のときは変更された行が無
かったことをあらわす。
・insert命令が成功したとき、レスポンスは次の構文を持つ。
'0' '1'
・命令が失敗したとき、レスポンスは命令に関わらず次の構文を持つ。
err '1' message
errは0以外の数値で、エラーコードをあらわす。messageは人間可読な
エラーメッセージ。ただしmessageが無いこともある。
Copyright (c) 2010 DeNA Co.,Ltd.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of DeNA Co.,Ltd. nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY DeNA Co.,Ltd. "AS IS" AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
EVENT SHALL DeNA Co.,Ltd. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
CXXFLAGS += -fimplicit-templates
pkgplugindir = $(PLUGIN_DIR)
noinst_HEADERS = database.hpp hstcpsvr.hpp hstcpsvr_worker.hpp mysql_incl.hpp
pkgplugin_LTLIBRARIES = handlersocket.la
handlersocket_la_LDFLAGS = -module ../libhsclient/libhsclient.la
handlersocket_la_CFLAGS = $(MYSQL_INC) $(MYSQL_CFLAGS) $(AM_CFLAGS) \
-I$(srcdir)/../libhsclient
handlersocket_la_CXXFLAGS = $(MYSQL_INC) $(MYSQL_CFLAGS) $(AM_CFLAGS) \
-I$(srcdir)/../libhsclient
handlersocket_la_SOURCES = database.cpp handlersocket.cpp \
hstcpsvr_worker.cpp hstcpsvr.cpp
MYSQL_INC = HANDLERSOCKET_MYSQL_INC
MYSQL_LIB = HANDLERSOCKET_MYSQL_LIB
CXX = g++ -Wall -g -fno-rtti -fno-exceptions -fPIC -DPIC
LIBS = $(MYSQL_LIB) -lhsclient -lpthread -lz
CXXFLAGS = -I/usr/include/handlersocket $(MYSQL_INC)
LDFLAGS =
CXXFLAGS += -O3 -DNDEBUG
HANDLERSOCKET_OBJS = database.o hstcpsvr.o hstcpsvr_worker.o
all: handlersocket.so
handlersocket.so: $(HANDLERSOCKET_OBJS) handlersocket.cpp
$(CXX) $(CXXFLAGS) -fno-strict-aliasing -shared $^ -o $@ $(LDFLAGS) \
-Wl,-soname -Wl,$@ $(LIBS)
clean:
rm -f *.a *.so *.o
LIBDIR = $(shell \
if [ -e /usr/lib64/mysql ]; then echo /usr/lib64; else echo /usr/lib; fi)
install: handlersocket.so
sudo sh -c 'ulimit -c unlimited ; /etc/init.d/mysql stop ; \
cp handlersocket.so handlersocket.so.cpy && \
mv handlersocket.so.cpy \
$(LIBDIR)/mysql/plugin/handlersocket.so && \
/etc/init.d/mysql start'
// vim:sw=2:ai
/*
* Copyright (C) 2010 DeNA Co.,Ltd.. All rights reserved.
* See COPYRIGHT.txt for details.
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "database.hpp"
#include "string_util.hpp"
#include "escape.hpp"
#include "mysql_incl.hpp"
#define DBG_KEY(x)
#define DBG_SHUT(x)
#define DBG_LOCK(x)
#define DBG_THR(x)
#define DBG_CMP(x)
#define DBG_FLD(x)
#define DBG_FILTER(x)
#define DBG_REFCNT(x)
#define DBG_DELETED
/* status variables */
unsigned long long int open_tables_count;
unsigned long long int close_tables_count;
unsigned long long int lock_tables_count;
unsigned long long int unlock_tables_count;
unsigned long long int index_exec_count;
namespace dena {
prep_stmt::prep_stmt()
: dbctx(0), table_id(static_cast<size_t>(-1)),
idxnum(static_cast<size_t>(-1))
{
}
prep_stmt::prep_stmt(dbcontext_i *c, size_t tbl, size_t idx,
const fields_type& rf, const fields_type& ff)
: dbctx(c), table_id(tbl), idxnum(idx), ret_fields(rf), filter_fields(ff)
{
if (dbctx) {
dbctx->table_addref(table_id);
}
}
prep_stmt::~prep_stmt()
{
if (dbctx) {
dbctx->table_release(table_id);
}
}
prep_stmt::prep_stmt(const prep_stmt& x)
: dbctx(x.dbctx), table_id(x.table_id), idxnum(x.idxnum),
ret_fields(x.ret_fields), filter_fields(x.filter_fields)
{
if (dbctx) {
dbctx->table_addref(table_id);
}
}
prep_stmt&
prep_stmt::operator =(const prep_stmt& x)
{
if (this != &x) {
if (dbctx) {
dbctx->table_release(table_id);
}
dbctx = x.dbctx;
table_id = x.table_id;
idxnum = x.idxnum;
ret_fields = x.ret_fields;
filter_fields = x.filter_fields;
if (dbctx) {
dbctx->table_addref(table_id);
}
}
return *this;
}
struct database : public database_i, private noncopyable {
database(const config& c);
virtual ~database();
virtual dbcontext_ptr create_context(bool for_write) volatile;
virtual void stop() volatile;
virtual const config& get_conf() const volatile;
public:
int child_running;
private:
config conf;
};
struct tablevec_entry {
TABLE *table;
size_t refcount;
bool modified;
tablevec_entry() : table(0), refcount(0), modified(false) { }
};
struct expr_user_lock : private noncopyable {
expr_user_lock(THD *thd, int timeout)
: lck_key("handlersocket_wr", 16, &my_charset_latin1),
lck_timeout(timeout),
lck_func_get_lock(&lck_key, &lck_timeout),
lck_func_release_lock(&lck_key)
{
lck_key.fix_fields(thd, 0);
lck_timeout.fix_fields(thd, 0);
lck_func_get_lock.fix_fields(thd, 0);
lck_func_release_lock.fix_fields(thd, 0);
}
long long get_lock() {
return lck_func_get_lock.val_int();
}
long long release_lock() {
return lck_func_release_lock.val_int();
}
private:
Item_string lck_key;
Item_int lck_timeout;
Item_func_get_lock lck_func_get_lock;
Item_func_release_lock lck_func_release_lock;
};
struct dbcontext : public dbcontext_i, private noncopyable {
dbcontext(volatile database *d, bool for_write);
virtual ~dbcontext();
virtual void init_thread(const void *stack_botton,
volatile int& shutdown_flag);
virtual void term_thread();
virtual bool check_alive();
virtual void lock_tables_if();
virtual void unlock_tables_if();
virtual bool get_commit_error();
virtual void clear_error();
virtual void close_tables_if();
virtual void table_addref(size_t tbl_id);
virtual void table_release(size_t tbl_id);
virtual void cmd_open_index(dbcallback_i& cb, size_t pst_id, const char *dbn,
const char *tbl, const char *idx, const char *retflds,
const char *filflds);
virtual void cmd_exec_on_index(dbcallback_i& cb, const cmd_exec_args& args);
virtual void set_statistics(size_t num_conns, size_t num_active);
private:
int set_thread_message(const char *fmt, ...)
__attribute__((format (printf, 2, 3)));
bool parse_fields(TABLE *const table, const char *str,
prep_stmt::fields_type& flds);
void cmd_insert_internal(dbcallback_i& cb, const prep_stmt& pst,
const string_ref *fvals, size_t fvalslen);
void cmd_sql_internal(dbcallback_i& cb, const prep_stmt& pst,
const string_ref *fvals, size_t fvalslen);
void cmd_find_internal(dbcallback_i& cb, const prep_stmt& pst,
ha_rkey_function find_flag, const cmd_exec_args& args);
size_t calc_filter_buf_size(TABLE *table, const prep_stmt& pst,
const record_filter *filters);
bool fill_filter_buf(TABLE *table, const prep_stmt& pst,
const record_filter *filters, uchar *filter_buf, size_t len);
int check_filter(dbcallback_i& cb, TABLE *table, const prep_stmt& pst,
const record_filter *filters, const uchar *filter_buf);
void resp_record(dbcallback_i& cb, TABLE *const table, const prep_stmt& pst);
void dump_record(dbcallback_i& cb, TABLE *const table, const prep_stmt& pst);
int modify_record(dbcallback_i& cb, TABLE *const table,
const prep_stmt& pst, const cmd_exec_args& args, char mod_op,
size_t& modified_count);
private:
typedef std::vector<tablevec_entry> table_vec_type;
typedef std::pair<std::string, std::string> table_name_type;
typedef std::map<table_name_type, size_t> table_map_type;
private:
volatile database *const dbref;
bool for_write_flag;
THD *thd;
MYSQL_LOCK *lock;
bool lock_failed;
std::auto_ptr<expr_user_lock> user_lock;
int user_level_lock_timeout;
bool user_level_lock_locked;
bool commit_error;
std::vector<char> info_message_buf;
table_vec_type table_vec;
table_map_type table_map;
#if MYSQL_VERSION_ID >= 50505
MDL_request *mdl_request;
#else
void *mdl_request;
#endif
};
database::database(const config& c)
: child_running(1), conf(c)
{
}
database::~database()
{
}
dbcontext_ptr
database::create_context(bool for_write) volatile
{
return dbcontext_ptr(new dbcontext(this, for_write));
}
void
database::stop() volatile
{
child_running = false;
}
const config&
database::get_conf() const volatile
{
return const_cast<const config&>(conf);
}
database_ptr
database_i::create(const config& conf)
{
return database_ptr(new database(conf));
}
dbcontext::dbcontext(volatile database *d, bool for_write)
: dbref(d), for_write_flag(for_write), thd(0), lock(0), lock_failed(false),
user_level_lock_timeout(0), user_level_lock_locked(false),
commit_error(false), mdl_request(0)
{
info_message_buf.resize(8192);
user_level_lock_timeout = d->get_conf().get_int("wrlock_timeout", 12);
}
dbcontext::~dbcontext()
{
}
namespace {
int
wait_server_to_start(THD *thd, volatile int& shutdown_flag)
{
int r = 0;
DBG_SHUT(fprintf(stderr, "HNDSOCK wsts\n"));
pthread_mutex_lock(&LOCK_server_started);
while (!mysqld_server_started) {
timespec abstime = { };
set_timespec(abstime, 1);
pthread_cond_timedwait(&COND_server_started, &LOCK_server_started,
&abstime);
pthread_mutex_unlock(&LOCK_server_started);
pthread_mutex_lock(&thd->mysys_var->mutex);
THD::killed_state st = thd->killed;
pthread_mutex_unlock(&thd->mysys_var->mutex);
DBG_SHUT(fprintf(stderr, "HNDSOCK wsts kst %d\n", (int)st));
pthread_mutex_lock(&LOCK_server_started);
if (st != THD::NOT_KILLED) {
DBG_SHUT(fprintf(stderr, "HNDSOCK wsts kst %d break\n", (int)st));
r = -1;
break;
}
if (shutdown_flag) {
DBG_SHUT(fprintf(stderr, "HNDSOCK wsts kst shut break\n"));
r = -1;
break;
}
}
pthread_mutex_unlock(&LOCK_server_started);
DBG_SHUT(fprintf(stderr, "HNDSOCK wsts done\n"));
return r;
}
}; // namespace
void
dbcontext::init_thread(const void *stack_bottom, volatile int& shutdown_flag)
{
DBG_THR(fprintf(stderr, "HNDSOCK init thread\n"));
{
my_thread_init();
thd = new THD;
thd->thread_stack = (char *)stack_bottom;
DBG_THR(const size_t of = (char *)(&thd->thread_stack) - (char *)thd);
DBG_THR(fprintf(stderr, "thread_stack = %p sz=%zu of=%zu\n",
thd->thread_stack, sizeof(THD), of));
thd->store_globals();
thd->system_thread = static_cast<enum_thread_type>(1<<30UL);
const NET v = { 0 };
thd->net = v;
if (for_write_flag) {
#if MYSQL_VERSION_ID >= 50505
thd->variables.option_bits |= OPTION_BIN_LOG;
#else
thd->options |= OPTION_BIN_LOG;
#endif
safeFree(thd->db);
thd->db = 0;
thd->db = my_strdup("handlersocket", MYF(0));
}
my_pthread_setspecific_ptr(THR_THD, thd);
DBG_THR(fprintf(stderr, "HNDSOCK x0 %p\n", thd));
}
{
pthread_mutex_lock(&LOCK_thread_count);
thd->thread_id = thread_id++;
threads.append(thd);
++thread_count;
pthread_mutex_unlock(&LOCK_thread_count);
}
DBG_THR(fprintf(stderr, "HNDSOCK init thread wsts\n"));
wait_server_to_start(thd, shutdown_flag);
DBG_THR(fprintf(stderr, "HNDSOCK init thread done\n"));
thd_proc_info(thd, &info_message_buf[0]);
set_thread_message("hs:listening");
DBG_THR(fprintf(stderr, "HNDSOCK x1 %p\n", thd));
#if MYSQL_VERSION_ID >= 50508
mdl_request = new(thd->mem_root) MDL_request;
mdl_request->init(MDL_key::TABLE, "", "",
for_write_flag ? MDL_SHARED_WRITE : MDL_SHARED_READ, MDL_STATEMENT);
#elif MYSQL_VERSION_ID >= 50505
mdl_request = MDL_request::create(MDL_key::TABLE, "", "",
for_write_flag ? MDL_SHARED_WRITE : MDL_SHARED_READ, thd->mem_root);
#endif
lex_start(thd);
user_lock.reset(new expr_user_lock(thd, user_level_lock_timeout));
}
int
dbcontext::set_thread_message(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
const int n = vsnprintf(&info_message_buf[0], info_message_buf.size(),
fmt, ap);
va_end(ap);
return n;
}
void
dbcontext::term_thread()
{
DBG_THR(fprintf(stderr, "HNDSOCK thread end %p\n", thd));
unlock_tables_if();
my_pthread_setspecific_ptr(THR_THD, 0);
{
pthread_mutex_lock(&LOCK_thread_count);
delete thd;
thd = 0;
--thread_count;
pthread_mutex_unlock(&LOCK_thread_count);
my_thread_end();
}
}
bool
dbcontext::check_alive()
{
pthread_mutex_lock(&thd->mysys_var->mutex);
THD::killed_state st = thd->killed;
pthread_mutex_unlock(&thd->mysys_var->mutex);
DBG_SHUT(fprintf(stderr, "chk HNDSOCK kst %p %p %d %zu\n", thd, &thd->killed,
(int)st, sizeof(*thd)));
if (st != THD::NOT_KILLED) {
DBG_SHUT(fprintf(stderr, "chk HNDSOCK kst %d break\n", (int)st));
return false;
}
return true;
}
void
dbcontext::lock_tables_if()
{
if (lock_failed) {
return;
}
if (for_write_flag && !user_level_lock_locked) {
if (user_lock->get_lock()) {
user_level_lock_locked = true;
} else {
lock_failed = true;
return;
}
}
if (lock == 0) {
const size_t num_max = table_vec.size();
TABLE *tables[num_max ? num_max : 1]; /* GNU */
size_t num_open = 0;
for (size_t i = 0; i < num_max; ++i) {
if (table_vec[i].refcount > 0) {
tables[num_open++] = table_vec[i].table;
}
table_vec[i].modified = false;
}
#if MYSQL_VERSION_ID >= 50505
lock = thd->lock = mysql_lock_tables(thd, &tables[0], num_open, 0);
#else
bool need_reopen= false;
lock = thd->lock = mysql_lock_tables(thd, &tables[0], num_open,
MYSQL_LOCK_NOTIFY_IF_NEED_REOPEN, &need_reopen);
#endif
statistic_increment(lock_tables_count, &LOCK_status);
thd_proc_info(thd, &info_message_buf[0]);
DENA_VERBOSE(100, fprintf(stderr, "HNDSOCK lock tables %p %p %zu %zu\n",
thd, lock, num_max, num_open));
if (lock == 0) {
lock_failed = true;
DENA_VERBOSE(10, fprintf(stderr, "HNDSOCK failed to lock tables %p\n",
thd));
}
if (for_write_flag) {
#if MYSQL_VERSION_ID >= 50505
thd->set_current_stmt_binlog_format_row();
#else
thd->current_stmt_binlog_row_based = 1;
#endif
}
}
DBG_LOCK(fprintf(stderr, "HNDSOCK tblnum=%d\n", (int)tblnum));
}
void
dbcontext::unlock_tables_if()
{
if (lock != 0) {
DENA_VERBOSE(100, fprintf(stderr, "HNDSOCK unlock tables\n"));
if (for_write_flag) {
for (size_t i = 0; i < table_vec.size(); ++i) {
if (table_vec[i].modified) {
query_cache_invalidate3(thd, table_vec[i].table, 0);
/* invalidate immediately */
}
}
bool suc = true;
#if MYSQL_VERSION_ID >= 50505
suc = (trans_commit_stmt(thd) == 0);
#else
suc = (ha_autocommit_or_rollback(thd, 0) == 0);
#endif
if (!suc) {
commit_error = true;
DENA_VERBOSE(10, fprintf(stderr,
"HNDSOCK unlock tables: commit failed\n"));
}
}
mysql_unlock_tables(thd, lock);
lock = thd->lock = 0;
statistic_increment(unlock_tables_count, &LOCK_status);
}
if (user_level_lock_locked) {
if (user_lock->release_lock()) {
user_level_lock_locked = false;
}
}
}
bool
dbcontext::get_commit_error()
{
return commit_error;
}
void
dbcontext::clear_error()
{
lock_failed = false;
commit_error = false;
}
void
dbcontext::close_tables_if()
{
unlock_tables_if();
if (!table_vec.empty()) {
DENA_VERBOSE(100, fprintf(stderr, "HNDSOCK close tables\n"));
close_thread_tables(thd);
statistic_increment(close_tables_count, &LOCK_status);
table_vec.clear();
table_map.clear();
}
}
void
dbcontext::table_addref(size_t tbl_id)
{
table_vec[tbl_id].refcount += 1;
DBG_REFCNT(fprintf(stderr, "%p %zu %zu addref\n", this, tbl_id,
table_vec[tbl_id].refcount));
}
void
dbcontext::table_release(size_t tbl_id)
{
table_vec[tbl_id].refcount -= 1;
DBG_REFCNT(fprintf(stderr, "%p %zu %zu release\n", this, tbl_id,
table_vec[tbl_id].refcount));
}
void
dbcontext::resp_record(dbcallback_i& cb, TABLE *const table,
const prep_stmt& pst)
{
char rwpstr_buf[64];
String rwpstr(rwpstr_buf, sizeof(rwpstr_buf), &my_charset_bin);
const prep_stmt::fields_type& rf = pst.get_ret_fields();
const size_t n = rf.size();
for (size_t i = 0; i < n; ++i) {
uint32_t fn = rf[i];
Field *const fld = table->field[fn];
DBG_FLD(fprintf(stderr, "fld=%p %zu\n", fld, fn));
if (fld->is_null()) {
/* null */
cb.dbcb_resp_entry(0, 0);
} else {
fld->val_str(&rwpstr, &rwpstr);
const size_t len = rwpstr.length();
if (len != 0) {
/* non-empty */
cb.dbcb_resp_entry(rwpstr.ptr(), rwpstr.length());
} else {
/* empty */
static const char empty_str[] = "";
cb.dbcb_resp_entry(empty_str, 0);
}
}
}
}
void
dbcontext::dump_record(dbcallback_i& cb, TABLE *const table,
const prep_stmt& pst)
{
char rwpstr_buf[64];
String rwpstr(rwpstr_buf, sizeof(rwpstr_buf), &my_charset_bin);
const prep_stmt::fields_type& rf = pst.get_ret_fields();
const size_t n = rf.size();
for (size_t i = 0; i < n; ++i) {
uint32_t fn = rf[i];
Field *const fld = table->field[fn];
if (fld->is_null()) {
/* null */
cb.dbcb_resp_entry(0, 0);
fprintf(stderr, "NULL");
} else {
fld->val_str(&rwpstr, &rwpstr);
const std::string s(rwpstr.ptr(), rwpstr.length());
fprintf(stderr, "[%s]", s.c_str());
}
}
fprintf(stderr, "\n");
}
int
dbcontext::modify_record(dbcallback_i& cb, TABLE *const table,
const prep_stmt& pst, const cmd_exec_args& args, char mod_op,
size_t& modified_count)
{
if (mod_op == 'U') {
/* update */
handler *const hnd = table->file;
uchar *const buf = table->record[0];
store_record(table, record[1]);
const prep_stmt::fields_type& rf = pst.get_ret_fields();
const size_t n = rf.size();
for (size_t i = 0; i < n; ++i) {
const string_ref& nv = args.uvals[i];
uint32_t fn = rf[i];
Field *const fld = table->field[fn];
if (nv.begin() == 0) {
fld->set_null();
} else {
fld->set_notnull();
fld->store(nv.begin(), nv.size(), &my_charset_bin);
}
}
table_vec[pst.get_table_id()].modified = true;
const int r = hnd->ha_update_row(table->record[1], buf);
if (r != 0 && r != HA_ERR_RECORD_IS_THE_SAME) {
return r;
}
++modified_count; /* TODO: HA_ERR_RECORD_IS_THE_SAME? */
} else if (mod_op == 'D') {
/* delete */
handler *const hnd = table->file;
table_vec[pst.get_table_id()].modified = true;
const int r = hnd->ha_delete_row(table->record[0]);
if (r != 0) {
return r;
}
++modified_count;
} else if (mod_op == '+' || mod_op == '-') {
/* increment/decrement */
handler *const hnd = table->file;
uchar *const buf = table->record[0];
store_record(table, record[1]);
const prep_stmt::fields_type& rf = pst.get_ret_fields();
const size_t n = rf.size();
size_t i = 0;
for (i = 0; i < n; ++i) {
const string_ref& nv = args.uvals[i];
uint32_t fn = rf[i];
Field *const fld = table->field[fn];
if (fld->is_null() || nv.begin() == 0) {
continue;
}
const long long pval = fld->val_int();
const long long llv = atoll_nocheck(nv.begin(), nv.end());
/* TODO: llv == 0? */
long long nval = 0;
if (mod_op == '+') {
/* increment */
nval = pval + llv;
} else {
/* decrement */
nval = pval - llv;
if ((pval < 0 && nval > 0) || (pval > 0 && nval < 0)) {
break; /* don't modify */
}
if ((pval < 0) != (nval < 0)) {
nval = 0; /* crip */
}
}
fld->store(nval, false);
}
if (i == n) {
/* modify */
table_vec[pst.get_table_id()].modified = true;
const int r = hnd->ha_update_row(table->record[1], buf);
if (r != 0 && r != HA_ERR_RECORD_IS_THE_SAME) {
return r;
}
++modified_count;
}
}
return 0;
}
void
dbcontext::cmd_insert_internal(dbcallback_i& cb, const prep_stmt& pst,
const string_ref *fvals, size_t fvalslen)
{
if (!for_write_flag) {
return cb.dbcb_resp_short(2, "readonly");
}
lock_tables_if();
if (lock == 0) {
return cb.dbcb_resp_short(2, "lock_tables");
}
if (pst.get_table_id() >= table_vec.size()) {
return cb.dbcb_resp_short(2, "tblnum");
}
TABLE *const table = table_vec[pst.get_table_id()].table;
handler *const hnd = table->file;
uchar *const buf = table->record[0];
empty_record(table);
memset(buf, 0, table->s->null_bytes); /* clear null flags */
Field **fld = table->field;
size_t i = 0;
for (; *fld && i < fvalslen; ++fld, ++i) {
if (fvals[i].begin() == 0) {
(*fld)->set_null();
} else {
(*fld)->store(fvals[i].begin(), fvals[i].size(), &my_charset_bin);
}
}
table->next_number_field = table->found_next_number_field;
const int r = hnd->ha_write_row(buf);
table->next_number_field = 0;
table_vec[pst.get_table_id()].modified = true;
return cb.dbcb_resp_short(r != 0 ? 1 : 0, "");
}
void
dbcontext::cmd_sql_internal(dbcallback_i& cb, const prep_stmt& pst,
const string_ref *fvals, size_t fvalslen)
{
if (fvalslen < 1) {
return cb.dbcb_resp_short(2, "syntax");
}
return cb.dbcb_resp_short(2, "notimpl");
}
void
dbcontext::cmd_find_internal(dbcallback_i& cb, const prep_stmt& pst,
ha_rkey_function find_flag, const cmd_exec_args& args)
{
const bool debug_out = (verbose_level >= 100);
bool need_resp_record = true;
char mod_op = 0;
const string_ref& mod_op_str = args.mod_op;
if (mod_op_str.size() != 0) {
if (!for_write_flag) {
return cb.dbcb_resp_short(2, "readonly");
}
mod_op = mod_op_str.begin()[0];
need_resp_record = mod_op_str.size() > 1 && mod_op_str.begin()[1] == '?';
switch (mod_op) {
case 'U': /* update */
case 'D': /* delete */
case '+': /* increment */
case '-': /* decrement */
break;
default:
if (debug_out) {
fprintf(stderr, "unknown modop: %c\n", mod_op);
}
return cb.dbcb_resp_short(2, "modop");
}
}
lock_tables_if();
if (lock == 0) {
return cb.dbcb_resp_short(2, "lock_tables");
}
if (pst.get_table_id() >= table_vec.size()) {
return cb.dbcb_resp_short(2, "tblnum");
}
TABLE *const table = table_vec[pst.get_table_id()].table;
/* keys */
if (pst.get_idxnum() >= table->s->keys) {
return cb.dbcb_resp_short(2, "idxnum");
}
KEY& kinfo = table->key_info[pst.get_idxnum()];
if (args.kvalslen > kinfo.key_parts) {
return cb.dbcb_resp_short(2, "kpnum");
}
uchar key_buf[kinfo.key_length]; /* GNU */
size_t kplen_sum = 0;
{
DBG_KEY(fprintf(stderr, "SLOW\n"));
for (size_t i = 0; i < args.kvalslen; ++i) {
const KEY_PART_INFO & kpt = kinfo.key_part[i];
const string_ref& kval = args.kvals[i];
if (kval.begin() == 0) {
kpt.field->set_null();
} else {
kpt.field->set_notnull();
}
kpt.field->store(kval.begin(), kval.size(), &my_charset_bin);
kplen_sum += kpt.length;
}
key_copy(key_buf, table->record[0], &kinfo, kplen_sum);
}
/* filters */
uchar *filter_buf = 0;
if (args.filters != 0) {
const size_t filter_buf_len = calc_filter_buf_size(table, pst,
args.filters);
filter_buf = reinterpret_cast<uchar *>(alloca(filter_buf_len));
/* FIXME: TEST */
if (!fill_filter_buf(table, pst, args.filters, filter_buf,
filter_buf_len)) {
return cb.dbcb_resp_short(2, "filterblob");
}
}
/* handler */
table->read_set = &table->s->all_set;
handler *const hnd = table->file;
if (!for_write_flag) {
hnd->init_table_handle_for_HANDLER();
}
hnd->ha_index_or_rnd_end();
hnd->ha_index_init(pst.get_idxnum(), 1);
#if 0
statistic_increment(index_exec_count, &LOCK_status);
#endif
if (need_resp_record) {
cb.dbcb_resp_begin(pst.get_ret_fields().size());
}
const uint32_t limit = args.limit ? args.limit : 1;
uint32_t skip = args.skip;
size_t modified_count = 0;
int r = 0;
for (uint32_t i = 0; i < limit + skip; ++i) {
if (i == 0) {
const key_part_map kpm = (1U << args.kvalslen) - 1;
r = hnd->ha_index_read_map(table->record[0], key_buf, kpm, find_flag);
} else {
switch (find_flag) {
case HA_READ_BEFORE_KEY:
case HA_READ_KEY_OR_PREV:
r = hnd->ha_index_prev(table->record[0]);
break;
case HA_READ_AFTER_KEY:
case HA_READ_KEY_OR_NEXT:
r = hnd->ha_index_next(table->record[0]);
break;
case HA_READ_KEY_EXACT:
r = hnd->ha_index_next_same(table->record[0], key_buf, kplen_sum);
break;
default:
r = HA_ERR_END_OF_FILE; /* to finish the loop */
break;
}
}
if (debug_out) {
fprintf(stderr, "r=%d\n", r);
if (r == 0 || r == HA_ERR_RECORD_DELETED) {
dump_record(cb, table, pst);
}
}
int filter_res = 0;
if (r != 0) {
/* no-count */
} else if (args.filters != 0 && (filter_res = check_filter(cb, table,
pst, args.filters, filter_buf)) != 0) {
if (filter_res < 0) {
break;
}
} else if (skip > 0) {
--skip;
} else {
if (need_resp_record) {
resp_record(cb, table, pst);
}
if (mod_op != 0) {
r = modify_record(cb, table, pst, args, mod_op, modified_count);
}
}
if (r != 0 && r != HA_ERR_RECORD_DELETED) {
break;
}
}
hnd->ha_index_or_rnd_end();
if (r != 0 && r != HA_ERR_RECORD_DELETED && r != HA_ERR_KEY_NOT_FOUND &&
r != HA_ERR_END_OF_FILE) {
/* failed */
if (need_resp_record) {
/* revert dbcb_resp_begin() and dbcb_resp_entry() */
cb.dbcb_resp_cancel();
}
cb.dbcb_resp_short_num(2, r);
} else {
/* succeeded */
if (need_resp_record) {
cb.dbcb_resp_end();
} else {
cb.dbcb_resp_short_num(0, modified_count);
}
}
}
size_t
dbcontext::calc_filter_buf_size(TABLE *table, const prep_stmt& pst,
const record_filter *filters)
{
size_t filter_buf_len = 0;
for (const record_filter *f = filters; f->op.begin() != 0; ++f) {
if (f->val.begin() == 0) {
continue;
}
const uint32_t fn = pst.get_filter_fields()[f->ff_offset];
filter_buf_len += table->field[fn]->pack_length();
}
return filter_buf_len;
}
bool
dbcontext::fill_filter_buf(TABLE *table, const prep_stmt& pst,
const record_filter *filters, uchar *filter_buf, size_t len)
{
memset(filter_buf, 0, len);
size_t pos = 0;
for (const record_filter *f = filters; f->op.begin() != 0; ++f) {
if (f->val.begin() == 0) {
continue;
}
const uint32_t fn = pst.get_filter_fields()[f->ff_offset];
Field *const fld = table->field[fn];
if ((fld->flags & BLOB_FLAG) != 0) {
return false;
}
fld->store(f->val.begin(), f->val.size(), &my_charset_bin);
const size_t packlen = fld->pack_length();
memcpy(filter_buf + pos, fld->ptr, packlen);
pos += packlen;
}
return true;
}
int
dbcontext::check_filter(dbcallback_i& cb, TABLE *table, const prep_stmt& pst,
const record_filter *filters, const uchar *filter_buf)
{
DBG_FILTER(fprintf(stderr, "check_filter\n"));
size_t pos = 0;
for (const record_filter *f = filters; f->op.begin() != 0; ++f) {
const string_ref& op = f->op;
const string_ref& val = f->val;
const uint32_t fn = pst.get_filter_fields()[f->ff_offset];
Field *const fld = table->field[fn];
const size_t packlen = fld->pack_length();
const uchar *const bval = filter_buf + pos;
int cv = 0;
if (fld->is_null()) {
cv = (val.begin() == 0) ? 0 : -1;
} else {
cv = (val.begin() == 0) ? 1 : fld->cmp(bval);
}
DBG_FILTER(fprintf(stderr, "check_filter cv=%d\n", cv));
bool cond = true;
if (op.size() == 1) {
switch (op.begin()[0]) {
case '>':
DBG_FILTER(fprintf(stderr, "check_filter op: >\n"));
cond = (cv > 0);
break;
case '<':
DBG_FILTER(fprintf(stderr, "check_filter op: <\n"));
cond = (cv < 0);
break;
case '=':
DBG_FILTER(fprintf(stderr, "check_filter op: =\n"));
cond = (cv == 0);
break;
default:
DBG_FILTER(fprintf(stderr, "check_filter op: unknown\n"));
cond = false; /* FIXME: error */
break;
}
} else if (op.size() == 2 && op.begin()[1] == '=') {
switch (op.begin()[0]) {
case '>':
DBG_FILTER(fprintf(stderr, "check_filter op: >=\n"));
cond = (cv >= 0);
break;
case '<':
DBG_FILTER(fprintf(stderr, "check_filter op: <=\n"));
cond = (cv <= 0);
break;
case '!':
DBG_FILTER(fprintf(stderr, "check_filter op: !=\n"));
cond = (cv != 0);
break;
default:
DBG_FILTER(fprintf(stderr, "check_filter op: unknown\n"));
cond = false; /* FIXME: error */
break;
}
}
DBG_FILTER(fprintf(stderr, "check_filter cond: %d\n", (int)cond));
if (!cond) {
return (f->filter_type == record_filter_type_skip) ? 1 : -1;
}
if (val.begin() != 0) {
pos += packlen;
}
}
return 0;
}
void
dbcontext::cmd_open_index(dbcallback_i& cb, size_t pst_id, const char *dbn,
const char *tbl, const char *idx, const char *retflds, const char *filflds)
{
unlock_tables_if();
const table_name_type k = std::make_pair(std::string(dbn), std::string(tbl));
const table_map_type::const_iterator iter = table_map.find(k);
uint32_t tblnum = 0;
if (iter != table_map.end()) {
tblnum = iter->second;
DBG_CMP(fprintf(stderr, "HNDSOCK k=%s tblnum=%d\n", k.c_str(),
(int)tblnum));
} else {
TABLE_LIST tables;
TABLE *table = 0;
bool refresh = true;
const thr_lock_type lock_type = for_write_flag ? TL_WRITE : TL_READ;
#if MYSQL_VERSION_ID >= 50505
tables.init_one_table(dbn, strlen(dbn), tbl, strlen(tbl), tbl,
lock_type);
tables.mdl_request = mdl_request;
Open_table_context ot_act(thd, MYSQL_OPEN_REOPEN);
if (!open_table(thd, &tables, thd->mem_root, &ot_act)) {
table = tables.table;
}
#else
tables.init_one_table(dbn, tbl, lock_type);
table = open_table(thd, &tables, thd->mem_root, &refresh,
OPEN_VIEW_NO_PARSE);
#endif
if (table == 0) {
DENA_VERBOSE(10, fprintf(stderr,
"HNDSOCK failed to open %p [%s] [%s] [%d]\n",
thd, dbn, tbl, static_cast<int>(refresh)));
return cb.dbcb_resp_short(2, "open_table");
}
statistic_increment(open_tables_count, &LOCK_status);
table->reginfo.lock_type = lock_type;
table->use_all_columns();
tblnum = table_vec.size();
tablevec_entry e;
e.table = table;
table_vec.push_back(e);
table_map[k] = tblnum;
}
size_t idxnum = static_cast<size_t>(-1);
if (idx[0] >= '0' && idx[0] <= '9') {
/* numeric */
TABLE *const table = table_vec[tblnum].table;
idxnum = atoi(idx);
if (idxnum >= table->s->keys) {
return cb.dbcb_resp_short(2, "idxnum");
}
} else {
const char *const idx_name_to_open = idx[0] == '\0' ? "PRIMARY" : idx;
TABLE *const table = table_vec[tblnum].table;
for (uint i = 0; i < table->s->keys; ++i) {
KEY& kinfo = table->key_info[i];
if (strcmp(kinfo.name, idx_name_to_open) == 0) {
idxnum = i;
break;
}
}
}
if (idxnum == size_t(-1)) {
return cb.dbcb_resp_short(2, "idxnum");
}
prep_stmt::fields_type rf;
prep_stmt::fields_type ff;
if (!parse_fields(table_vec[tblnum].table, retflds, rf)) {
return cb.dbcb_resp_short(2, "fld");
}
if (!parse_fields(table_vec[tblnum].table, filflds, ff)) {
return cb.dbcb_resp_short(2, "fld");
}
prep_stmt p(this, tblnum, idxnum, rf, ff);
cb.dbcb_set_prep_stmt(pst_id, p);
return cb.dbcb_resp_short(0, "");
}
bool
dbcontext::parse_fields(TABLE *const table, const char *str,
prep_stmt::fields_type& flds)
{
string_ref flds_sr(str, strlen(str));
std::vector<string_ref> fldnms;
if (flds_sr.size() != 0) {
split(',', flds_sr, fldnms);
}
for (size_t i = 0; i < fldnms.size(); ++i) {
Field **fld = 0;
size_t j = 0;
for (fld = table->field; *fld; ++fld, ++j) {
DBG_FLD(fprintf(stderr, "f %s\n", (*fld)->field_name));
string_ref fn((*fld)->field_name, strlen((*fld)->field_name));
if (fn == fldnms[i]) {
break;
}
}
if (*fld == 0) {
DBG_FLD(fprintf(stderr, "UNKNOWN FLD %s [%s]\n", retflds,
std::string(fldnms[i].begin(), fldnms[i].size()).c_str()));
return false;
}
DBG_FLD(fprintf(stderr, "FLD %s %zu\n", (*fld)->field_name, j));
flds.push_back(j);
}
return true;
}
enum db_write_op {
db_write_op_none = 0,
db_write_op_insert = 1,
db_write_op_sql = 2,
};
void
dbcontext::cmd_exec_on_index(dbcallback_i& cb, const cmd_exec_args& args)
{
const prep_stmt& p = *args.pst;
if (p.get_table_id() == static_cast<size_t>(-1)) {
return cb.dbcb_resp_short(2, "stmtnum");
}
ha_rkey_function find_flag = HA_READ_KEY_EXACT;
db_write_op wrop = db_write_op_none;
if (args.op.size() == 1) {
switch (args.op.begin()[0]) {
case '=':
find_flag = HA_READ_KEY_EXACT;
break;
case '>':
find_flag = HA_READ_AFTER_KEY;
break;
case '<':
find_flag = HA_READ_BEFORE_KEY;
break;
case '+':
wrop = db_write_op_insert;
break;
case 'S':
wrop = db_write_op_sql;
break;
default:
return cb.dbcb_resp_short(1, "op");
}
} else if (args.op.size() == 2 && args.op.begin()[1] == '=') {
switch (args.op.begin()[0]) {
case '>':
find_flag = HA_READ_KEY_OR_NEXT;
break;
case '<':
find_flag = HA_READ_KEY_OR_PREV;
break;
default:
return cb.dbcb_resp_short(1, "op");
}
} else {
return cb.dbcb_resp_short(1, "op");
}
if (args.kvalslen <= 0) {
return cb.dbcb_resp_short(2, "klen");
}
switch (wrop) {
case db_write_op_none:
return cmd_find_internal(cb, p, find_flag, args);
case db_write_op_insert:
return cmd_insert_internal(cb, p, args.kvals, args.kvalslen);
case db_write_op_sql:
return cmd_sql_internal(cb, p, args.kvals, args.kvalslen);
}
}
void
dbcontext::set_statistics(size_t num_conns, size_t num_active)
{
thd_proc_info(thd, &info_message_buf[0]);
if (for_write_flag) {
set_thread_message("handlersocket: mode=wr, %zu conns, %zu active",
num_conns, num_active);
} else {
set_thread_message("handlersocket: mode=rd, %zu conns, %zu active",
num_conns, num_active);
}
}
};
// vim:sw=2:ai
/*
* Copyright (C) 2010 DeNA Co.,Ltd.. All rights reserved.
* See COPYRIGHT.txt for details.
*/
#ifndef DENA_DATABASE_HPP
#define DENA_DATABASE_HPP
#include <string>
#include <memory>
#include <vector>
#include <stdint.h>
#include "string_buffer.hpp"
#include "string_ref.hpp"
#include "config.hpp"
namespace dena {
struct database_i;
typedef std::auto_ptr<volatile database_i> database_ptr;
struct dbcontext_i;
typedef std::auto_ptr<dbcontext_i> dbcontext_ptr;
struct database_i {
virtual ~database_i() { }
virtual dbcontext_ptr create_context(bool for_write) volatile = 0;
virtual void stop() volatile = 0;
virtual const config& get_conf() const volatile = 0;
static database_ptr create(const config& conf);
};
struct prep_stmt {
typedef std::vector<uint32_t> fields_type;
private:
dbcontext_i *dbctx; /* must be valid while *this is alive */
size_t table_id; /* a prep_stmt object holds a refcount of the table */
size_t idxnum;
fields_type ret_fields;
fields_type filter_fields;
public:
prep_stmt();
prep_stmt(dbcontext_i *c, size_t tbl, size_t idx, const fields_type& rf,
const fields_type& ff);
~prep_stmt();
prep_stmt(const prep_stmt& x);
prep_stmt& operator =(const prep_stmt& x);
public:
size_t get_table_id() const { return table_id; }
size_t get_idxnum() const { return idxnum; }
const fields_type& get_ret_fields() const { return ret_fields; }
const fields_type& get_filter_fields() const { return filter_fields; }
};
struct dbcallback_i {
virtual ~dbcallback_i () { }
virtual void dbcb_set_prep_stmt(size_t pst_id, const prep_stmt& v) = 0;
virtual const prep_stmt *dbcb_get_prep_stmt(size_t pst_id) const = 0;
virtual void dbcb_resp_short(uint32_t code, const char *msg) = 0;
virtual void dbcb_resp_short_num(uint32_t code, uint32_t value) = 0;
virtual void dbcb_resp_begin(size_t num_flds) = 0;
virtual void dbcb_resp_entry(const char *fld, size_t fldlen) = 0;
virtual void dbcb_resp_end() = 0;
virtual void dbcb_resp_cancel() = 0;
};
enum record_filter_type {
record_filter_type_skip = 0,
record_filter_type_break = 1,
};
struct record_filter {
record_filter_type filter_type;
string_ref op;
uint32_t ff_offset; /* offset in filter_fields */
string_ref val;
record_filter() : filter_type(record_filter_type_skip), ff_offset(0) { }
};
struct cmd_exec_args {
const prep_stmt *pst;
string_ref op;
const string_ref *kvals;
size_t kvalslen;
uint32_t limit;
uint32_t skip;
string_ref mod_op;
const string_ref *uvals; /* size must be pst->retfieelds.size() */
const record_filter *filters;
cmd_exec_args() : pst(0), kvals(0), kvalslen(0), limit(0), skip(0),
uvals(0), filters(0) { }
};
struct dbcontext_i {
virtual ~dbcontext_i() { }
virtual void init_thread(const void *stack_bottom,
volatile int& shutdown_flag) = 0;
virtual void term_thread() = 0;
virtual bool check_alive() = 0;
virtual void lock_tables_if() = 0;
virtual void unlock_tables_if() = 0;
virtual bool get_commit_error() = 0;
virtual void clear_error() = 0;
virtual void close_tables_if() = 0;
virtual void table_addref(size_t tbl_id) = 0; /* TODO: hide */
virtual void table_release(size_t tbl_id) = 0; /* TODO: hide */
virtual void cmd_open_index(dbcallback_i& cb, size_t pst_id, const char *dbn,
const char *tbl, const char *idx, const char *retflds,
const char *filflds) = 0;
virtual void cmd_exec_on_index(dbcallback_i& cb, const cmd_exec_args& args)
= 0;
virtual void set_statistics(size_t num_conns, size_t num_active) = 0;
};
};
extern unsigned long long int open_tables_count;
extern unsigned long long int close_tables_count;
extern unsigned long long int lock_tables_count;
extern unsigned long long int unlock_tables_count;
#if 0
extern unsigned long long int index_exec_count;
#endif
#endif
// vim:sw=2:ai
/*
* Copyright (C) 2010 DeNA Co.,Ltd.. All rights reserved.
* See COPYRIGHT.txt for details.
*/
#include <memory>
#include <string>
#include <stdio.h>
#include "config.hpp"
#include "hstcpsvr.hpp"
#include "string_util.hpp"
#include "mysql_incl.hpp"
#define DBG_LOG \
if (dena::verbose_level >= 100) { \
fprintf(stderr, "%s %p\n", __PRETTY_FUNCTION__, this); \
}
#define DBG_DO(x) if (dena::verbose_level >= 100) { x; }
#define DBG_DIR(x)
using namespace dena;
static char *handlersocket_address = 0;
static char *handlersocket_port = 0;
static char *handlersocket_port_wr = 0;
static unsigned int handlersocket_epoll = 1;
static unsigned int handlersocket_threads = 32;
static unsigned int handlersocket_threads_wr = 1;
static unsigned int handlersocket_timeout = 30;
static unsigned int handlersocket_backlog = 32768;
static unsigned int handlersocket_sndbuf = 0;
static unsigned int handlersocket_rcvbuf = 0;
static unsigned int handlersocket_readsize = 0;
static unsigned int handlersocket_accept_balance = 0;
static unsigned int handlersocket_wrlock_timeout = 0;
static char *handlersocket_plain_secret = 0;
static char *handlersocket_plain_secret_wr = 0;
struct daemon_handlersocket_data {
hstcpsvr_ptr hssvr_rd;
hstcpsvr_ptr hssvr_wr;
};
static int
daemon_handlersocket_init(void *p)
{
DENA_VERBOSE(10, fprintf(stderr, "handlersocket: initialized\n"));
config conf;
conf["use_epoll"] = handlersocket_epoll ? "1" : "0";
if (handlersocket_address) {
conf["host"] = handlersocket_address;
}
if (handlersocket_port) {
conf["port"] = handlersocket_port;
}
/*
* unix domain socket
* conf["host"] = "/";
* conf["port"] = "/tmp/handlersocket";
*/
if (handlersocket_threads > 0) {
conf["num_threads"] = to_stdstring(handlersocket_threads);
} else {
conf["num_threads"] = "1";
}
conf["timeout"] = to_stdstring(handlersocket_timeout);
conf["listen_backlog"] = to_stdstring(handlersocket_backlog);
conf["sndbuf"] = to_stdstring(handlersocket_sndbuf);
conf["rcvbuf"] = to_stdstring(handlersocket_rcvbuf);
conf["readsize"] = to_stdstring(handlersocket_readsize);
conf["accept_balance"] = to_stdstring(handlersocket_accept_balance);
conf["wrlock_timeout"] = to_stdstring(handlersocket_wrlock_timeout);
std::auto_ptr<daemon_handlersocket_data> ap(new daemon_handlersocket_data);
if (handlersocket_port != 0 && handlersocket_port_wr != handlersocket_port) {
conf["port"] = handlersocket_port;
if (handlersocket_plain_secret) {
conf["plain_secret"] = handlersocket_plain_secret;
}
ap->hssvr_rd = hstcpsvr_i::create(conf);
ap->hssvr_rd->start_listen();
}
if (handlersocket_port_wr != 0) {
if (handlersocket_threads_wr > 0) {
conf["num_threads"] = to_stdstring(handlersocket_threads_wr);
}
conf["port"] = handlersocket_port_wr;
conf["for_write"] = "1";
conf["plain_secret"] = "";
if (handlersocket_plain_secret_wr) {
conf["plain_secret"] = handlersocket_plain_secret_wr;
}
ap->hssvr_wr = hstcpsvr_i::create(conf);
ap->hssvr_wr->start_listen();
}
st_plugin_int *const plugin = static_cast<st_plugin_int *>(p);
plugin->data = ap.release();
return 0;
}
static int
daemon_handlersocket_deinit(void *p)
{
DENA_VERBOSE(10, fprintf(stderr, "handlersocket: terminated\n"));
st_plugin_int *const plugin = static_cast<st_plugin_int *>(p);
daemon_handlersocket_data *ptr =
static_cast<daemon_handlersocket_data *>(plugin->data);
delete ptr;
return 0;
}
static struct st_mysql_daemon daemon_handlersocket_plugin = {
MYSQL_DAEMON_INTERFACE_VERSION
};
static MYSQL_SYSVAR_UINT(verbose, dena::verbose_level, 0,
"0..10000", 0, 0, 10 /* default */, 0, 10000, 0);
static MYSQL_SYSVAR_UINT(epoll, handlersocket_epoll, PLUGIN_VAR_READONLY,
"0..1", 0, 0, 1 /* default */, 0, 1, 0);
static MYSQL_SYSVAR_STR(address, handlersocket_address,
PLUGIN_VAR_READONLY | PLUGIN_VAR_MEMALLOC, "", NULL, NULL, NULL);
static MYSQL_SYSVAR_STR(port, handlersocket_port,
PLUGIN_VAR_READONLY | PLUGIN_VAR_MEMALLOC, "", NULL, NULL, NULL);
static MYSQL_SYSVAR_STR(port_wr, handlersocket_port_wr,
PLUGIN_VAR_READONLY | PLUGIN_VAR_MEMALLOC, "", NULL, NULL, NULL);
static MYSQL_SYSVAR_UINT(threads, handlersocket_threads, PLUGIN_VAR_READONLY,
"1..3000", 0, 0, 16 /* default */, 1, 3000, 0);
static MYSQL_SYSVAR_UINT(threads_wr, handlersocket_threads_wr,
PLUGIN_VAR_READONLY, "1..3000", 0, 0, 1 /* default */, 1, 3000, 0);
static MYSQL_SYSVAR_UINT(timeout, handlersocket_timeout, PLUGIN_VAR_READONLY,
"30..3600", 0, 0, 300 /* default */, 30, 3600, 0);
static MYSQL_SYSVAR_UINT(backlog, handlersocket_backlog, PLUGIN_VAR_READONLY,
"5..1000000", 0, 0, 32768 /* default */, 5, 1000000, 0);
static MYSQL_SYSVAR_UINT(sndbuf, handlersocket_sndbuf, PLUGIN_VAR_READONLY,
"0..16777216", 0, 0, 0 /* default */, 0, 16777216, 0);
static MYSQL_SYSVAR_UINT(rcvbuf, handlersocket_rcvbuf, PLUGIN_VAR_READONLY,
"0..16777216", 0, 0, 0 /* default */, 0, 16777216, 0);
static MYSQL_SYSVAR_UINT(readsize, handlersocket_readsize, PLUGIN_VAR_READONLY,
"0..16777216", 0, 0, 0 /* default */, 0, 16777216, 0);
static MYSQL_SYSVAR_UINT(accept_balance, handlersocket_accept_balance,
PLUGIN_VAR_READONLY, "0..10000", 0, 0, 0 /* default */, 0, 10000, 0);
static MYSQL_SYSVAR_UINT(wrlock_timeout, handlersocket_wrlock_timeout,
PLUGIN_VAR_READONLY, "0..3600", 0, 0, 12 /* default */, 0, 3600, 0);
static MYSQL_SYSVAR_STR(plain_secret, handlersocket_plain_secret,
PLUGIN_VAR_READONLY | PLUGIN_VAR_MEMALLOC, "", NULL, NULL, NULL);
static MYSQL_SYSVAR_STR(plain_secret_wr, handlersocket_plain_secret_wr,
PLUGIN_VAR_READONLY | PLUGIN_VAR_MEMALLOC, "", NULL, NULL, NULL);
/* warning: type-punning to incomplete type might break strict-aliasing
* rules */
static struct st_mysql_sys_var *daemon_handlersocket_system_variables[] = {
MYSQL_SYSVAR(verbose),
MYSQL_SYSVAR(address),
MYSQL_SYSVAR(port),
MYSQL_SYSVAR(port_wr),
MYSQL_SYSVAR(epoll),
MYSQL_SYSVAR(threads),
MYSQL_SYSVAR(threads_wr),
MYSQL_SYSVAR(timeout),
MYSQL_SYSVAR(backlog),
MYSQL_SYSVAR(sndbuf),
MYSQL_SYSVAR(rcvbuf),
MYSQL_SYSVAR(readsize),
MYSQL_SYSVAR(accept_balance),
MYSQL_SYSVAR(wrlock_timeout),
MYSQL_SYSVAR(plain_secret),
MYSQL_SYSVAR(plain_secret_wr),
0
};
static SHOW_VAR hs_status_variables[] = {
{"table_open", (char*) &open_tables_count, SHOW_LONGLONG},
{"table_close", (char*) &close_tables_count, SHOW_LONGLONG},
{"table_lock", (char*) &lock_tables_count, SHOW_LONGLONG},
{"table_unlock", (char*) &unlock_tables_count, SHOW_LONGLONG},
#if 0
{"index_exec", (char*) &index_exec_count, SHOW_LONGLONG},
#endif
{NullS, NullS, SHOW_LONG}
};
static int show_hs_vars(THD *thd, SHOW_VAR *var, char *buff)
{
var->type= SHOW_ARRAY;
var->value= (char *) &hs_status_variables;
return 0;
}
static SHOW_VAR daemon_handlersocket_status_variables[] = {
{"Hs", (char*) show_hs_vars, SHOW_FUNC},
{NullS, NullS, SHOW_LONG}
};
mysql_declare_plugin(handlersocket)
{
MYSQL_DAEMON_PLUGIN,
&daemon_handlersocket_plugin,
"handlersocket",
"higuchi dot akira at dena dot jp",
"",
PLUGIN_LICENSE_BSD,
daemon_handlersocket_init,
daemon_handlersocket_deinit,
0x0100 /* 1.0 */,
daemon_handlersocket_status_variables,
daemon_handlersocket_system_variables,
0
}
mysql_declare_plugin_end;
Summary: handlersocket plugin for mysql
Name: handlersocket
Version: HANDLERSOCKET_VERSION
Release: 1%{?dist}
Group: System Environment/Libraries
License: BSD
Source: handlersocket.tar.gz
Packager: Akira Higuchi <higuchi dot akira at dena dot jp>
BuildRoot: /var/tmp/%{name}-%{version}-root
%description
%prep
%setup -n %{name}
%define _use_internal_dependency_generator 0
%build
make -f Makefile.plain
%install
rm -rf $RPM_BUILD_ROOT
mkdir -p $RPM_BUILD_ROOT/%{_libdir}/mysql/plugin
install -m 755 handlersocket.so $RPM_BUILD_ROOT/%{_libdir}/mysql/plugin/
%files
%defattr(-, root, root)
%{_libdir}/mysql/plugin/*.so
// vim:sw=2:ai
/*
* Copyright (C) 2010 DeNA Co.,Ltd.. All rights reserved.
* See COPYRIGHT.txt for details.
*/
#include <stdlib.h>
#include <vector>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/resource.h>
#include "hstcpsvr.hpp"
#include "hstcpsvr_worker.hpp"
#include "thread.hpp"
#include "fatal.hpp"
#include "auto_ptrcontainer.hpp"
#define DBG(x)
namespace dena {
struct worker_throbj {
worker_throbj(const hstcpsvr_worker_arg& arg)
: worker(hstcpsvr_worker_i::create(arg)) { }
void operator ()() {
worker->run();
}
hstcpsvr_worker_ptr worker;
};
struct hstcpsvr : public hstcpsvr_i, private noncopyable {
hstcpsvr(const config& c);
~hstcpsvr();
virtual std::string start_listen();
private:
hstcpsvr_shared_c cshared;
volatile hstcpsvr_shared_v vshared;
typedef thread<worker_throbj> worker_thread_type;
typedef auto_ptrcontainer< std::vector<worker_thread_type *> > threads_type;
threads_type threads;
std::vector<unsigned int> thread_num_conns_vec;
private:
void stop_workers();
};
namespace {
void
check_nfile(size_t nfile)
{
struct rlimit rl;
const int r = getrlimit(RLIMIT_NOFILE, &rl);
if (r != 0) {
fatal_abort("check_nfile: getrlimit failed");
}
if (rl.rlim_cur < static_cast<rlim_t>(nfile + 1000)) {
fprintf(stderr,
"[Warning] handlersocket: open_files_limit is too small.\n");
}
}
};
hstcpsvr::hstcpsvr(const config& c)
: cshared(), vshared()
{
vshared.shutdown = 0;
cshared.conf = c; /* copy */
if (cshared.conf["port"] == "") {
cshared.conf["port"] = "9999";
}
cshared.num_threads = cshared.conf.get_int("num_threads", 32);
cshared.sockargs.nonblocking = cshared.conf.get_int("nonblocking", 1);
cshared.sockargs.use_epoll = cshared.conf.get_int("use_epoll", 1);
if (cshared.sockargs.use_epoll) {
cshared.sockargs.nonblocking = 1;
}
cshared.readsize = cshared.conf.get_int("readsize", 1);
cshared.nb_conn_per_thread = cshared.conf.get_int("conn_per_thread", 1024);
cshared.for_write_flag = cshared.conf.get_int("for_write", 0);
cshared.plain_secret = cshared.conf.get_str("plain_secret", "");
cshared.require_auth = !cshared.plain_secret.empty();
cshared.sockargs.set(cshared.conf);
cshared.dbptr = database_i::create(c);
check_nfile(cshared.num_threads * cshared.nb_conn_per_thread);
thread_num_conns_vec.resize(cshared.num_threads);
cshared.thread_num_conns = thread_num_conns_vec.empty()
? 0 : &thread_num_conns_vec[0];
}
hstcpsvr::~hstcpsvr()
{
stop_workers();
}
std::string
hstcpsvr::start_listen()
{
std::string err;
if (threads.size() != 0) {
return "start_listen: already running";
}
if (socket_bind(cshared.listen_fd, cshared.sockargs, err) != 0) {
return "bind: " + err;
}
DENA_VERBOSE(20, fprintf(stderr, "bind done\n"));
const size_t stack_size = std::max(
cshared.conf.get_int("stack_size", 1 * 1024LL * 1024), 8 * 1024LL * 1024);
for (long i = 0; i < cshared.num_threads; ++i) {
hstcpsvr_worker_arg arg;
arg.cshared = &cshared;
arg.vshared = &vshared;
arg.worker_id = i;
std::auto_ptr< thread<worker_throbj> > thr(
new thread<worker_throbj>(arg, stack_size));
threads.push_back_ptr(thr);
}
DENA_VERBOSE(20, fprintf(stderr, "threads created\n"));
for (size_t i = 0; i < threads.size(); ++i) {
threads[i]->start();
}
DENA_VERBOSE(20, fprintf(stderr, "threads started\n"));
return std::string();
}
void
hstcpsvr::stop_workers()
{
vshared.shutdown = 1;
for (size_t i = 0; i < threads.size(); ++i) {
threads[i]->join();
}
threads.clear();
}
hstcpsvr_ptr
hstcpsvr_i::create(const config& conf)
{
return hstcpsvr_ptr(new hstcpsvr(conf));
}
};
// vim:sw=2:ai
/*
* Copyright (C) 2010 DeNA Co.,Ltd.. All rights reserved.
* See COPYRIGHT.txt for details.
*/
#ifndef DENA_HSTCPSVR_HPP
#define DENA_HSTCPSVR_HPP
#include <memory>
#include <string>
#include <map>
#include "mutex.hpp"
#include "auto_file.hpp"
#include "database.hpp"
#include "config.hpp"
#include "socket.hpp"
namespace dena {
struct hstcpsvr_shared_c {
config conf;
long num_threads;
long nb_conn_per_thread;
bool for_write_flag;
bool require_auth;
std::string plain_secret;
int readsize;
socket_args sockargs;
auto_file listen_fd;
database_ptr dbptr;
volatile unsigned int *thread_num_conns; /* 0 .. num_threads-1 */
hstcpsvr_shared_c() : num_threads(0), nb_conn_per_thread(100),
for_write_flag(false), require_auth(false), readsize(0),
thread_num_conns(0) { }
};
struct hstcpsvr_shared_v : public mutex {
int shutdown;
hstcpsvr_shared_v() : shutdown(0) { }
};
struct hstcpsvr_i;
typedef std::auto_ptr<hstcpsvr_i> hstcpsvr_ptr;
struct hstcpsvr_i {
virtual ~hstcpsvr_i() { }
virtual std::string start_listen() = 0;
static hstcpsvr_ptr create(const config& conf);
};
};
#endif
// vim:sw=2:ai
/*
* Copyright (C) 2010 DeNA Co.,Ltd.. All rights reserved.
* See COPYRIGHT.txt for details.
*/
#include <netinet/in.h>
#include <errno.h>
#include <poll.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdexcept>
#include <signal.h>
#include <list>
#if __linux__
#include <sys/epoll.h>
#endif
#include "hstcpsvr_worker.hpp"
#include "string_buffer.hpp"
#include "auto_ptrcontainer.hpp"
#include "string_util.hpp"
#include "escape.hpp"
#define DBG_FD(x)
#define DBG_TR(x)
#define DBG_EP(x)
/* TODO */
#if !defined(__linux__) && !defined(__FreeBSD__) && !defined(MSG_NOSIGNAL)
#define MSG_NOSIGNAL 0
#endif
namespace dena {
struct dbconnstate {
string_buffer readbuf;
string_buffer writebuf;
std::vector<prep_stmt> prep_stmts;
size_t resp_begin_pos;
void reset() {
readbuf.clear();
writebuf.clear();
prep_stmts.clear();
resp_begin_pos = 0;
}
dbconnstate() : resp_begin_pos(0) { }
};
struct hstcpsvr_conn;
typedef auto_ptrcontainer< std::list<hstcpsvr_conn *> > hstcpsvr_conns_type;
struct hstcpsvr_conn : public dbcallback_i {
public:
auto_file fd;
sockaddr_storage addr;
socklen_t addr_len;
dbconnstate cstate;
std::string err;
size_t readsize;
bool nonblocking;
bool read_finished;
bool write_finished;
time_t nb_last_io;
hstcpsvr_conns_type::iterator conns_iter;
bool authorized;
public:
bool closed() const;
bool ok_to_close() const;
void reset();
int accept(const hstcpsvr_shared_c& cshared);
bool write_more(bool *more_r = 0);
bool read_more(bool *more_r = 0);
public:
virtual void dbcb_set_prep_stmt(size_t pst_id, const prep_stmt& v);
virtual const prep_stmt *dbcb_get_prep_stmt(size_t pst_id) const;
virtual void dbcb_resp_short(uint32_t code, const char *msg);
virtual void dbcb_resp_short_num(uint32_t code, uint32_t value);
virtual void dbcb_resp_begin(size_t num_flds);
virtual void dbcb_resp_entry(const char *fld, size_t fldlen);
virtual void dbcb_resp_end();
virtual void dbcb_resp_cancel();
public:
hstcpsvr_conn() : addr_len(sizeof(addr)), readsize(4096),
nonblocking(false), read_finished(false), write_finished(false),
nb_last_io(0), authorized(false) { }
};
bool
hstcpsvr_conn::closed() const
{
return fd.get() < 0;
}
bool
hstcpsvr_conn::ok_to_close() const
{
return write_finished || (read_finished && cstate.writebuf.size() == 0);
}
void
hstcpsvr_conn::reset()
{
addr = sockaddr_storage();
addr_len = sizeof(addr);
cstate.reset();
fd.reset();
read_finished = false;
write_finished = false;
}
int
hstcpsvr_conn::accept(const hstcpsvr_shared_c& cshared)
{
reset();
return socket_accept(cshared.listen_fd.get(), fd, cshared.sockargs, addr,
addr_len, err);
}
bool
hstcpsvr_conn::write_more(bool *more_r)
{
if (write_finished || cstate.writebuf.size() == 0) {
return false;
}
const size_t wlen = cstate.writebuf.size();
ssize_t len = send(fd.get(), cstate.writebuf.begin(), wlen, MSG_NOSIGNAL);
if (len <= 0) {
if (len == 0 || !nonblocking || errno != EWOULDBLOCK) {
cstate.writebuf.clear();
write_finished = true;
}
return false;
}
cstate.writebuf.erase_front(len);
/* FIXME: reallocate memory if too large */
if (more_r) {
*more_r = (static_cast<size_t>(len) == wlen);
}
return true;
}
bool
hstcpsvr_conn::read_more(bool *more_r)
{
if (read_finished) {
return false;
}
const size_t block_size = readsize > 4096 ? readsize : 4096;
char *wp = cstate.readbuf.make_space(block_size);
const ssize_t len = read(fd.get(), wp, block_size);
if (len <= 0) {
if (len == 0 || !nonblocking || errno != EWOULDBLOCK) {
read_finished = true;
}
return false;
}
cstate.readbuf.space_wrote(len);
if (more_r) {
*more_r = (static_cast<size_t>(len) == block_size);
}
return true;
}
void
hstcpsvr_conn::dbcb_set_prep_stmt(size_t pst_id, const prep_stmt& v)
{
if (cstate.prep_stmts.size() <= pst_id) {
cstate.prep_stmts.resize(pst_id + 1);
}
cstate.prep_stmts[pst_id] = v;
}
const prep_stmt *
hstcpsvr_conn::dbcb_get_prep_stmt(size_t pst_id) const
{
if (cstate.prep_stmts.size() <= pst_id) {
return 0;
}
return &cstate.prep_stmts[pst_id];
}
void
hstcpsvr_conn::dbcb_resp_short(uint32_t code, const char *msg)
{
write_ui32(cstate.writebuf, code);
const size_t msglen = strlen(msg);
if (msglen != 0) {
cstate.writebuf.append_literal("\t1\t");
cstate.writebuf.append(msg, msg + msglen);
} else {
cstate.writebuf.append_literal("\t1");
}
cstate.writebuf.append_literal("\n");
}
void
hstcpsvr_conn::dbcb_resp_short_num(uint32_t code, uint32_t value)
{
write_ui32(cstate.writebuf, code);
cstate.writebuf.append_literal("\t1\t");
write_ui32(cstate.writebuf, value);
cstate.writebuf.append_literal("\n");
}
void
hstcpsvr_conn::dbcb_resp_begin(size_t num_flds)
{
cstate.resp_begin_pos = cstate.writebuf.size();
cstate.writebuf.append_literal("0\t");
write_ui32(cstate.writebuf, num_flds);
}
void
hstcpsvr_conn::dbcb_resp_entry(const char *fld, size_t fldlen)
{
if (fld != 0) {
cstate.writebuf.append_literal("\t");
escape_string(cstate.writebuf, fld, fld + fldlen);
} else {
static const char t[] = "\t\0";
cstate.writebuf.append(t, t + 2);
}
}
void
hstcpsvr_conn::dbcb_resp_end()
{
cstate.writebuf.append_literal("\n");
cstate.resp_begin_pos = 0;
}
void
hstcpsvr_conn::dbcb_resp_cancel()
{
cstate.writebuf.resize(cstate.resp_begin_pos);
cstate.resp_begin_pos = 0;
}
struct hstcpsvr_worker : public hstcpsvr_worker_i, private noncopyable {
hstcpsvr_worker(const hstcpsvr_worker_arg& arg);
virtual void run();
private:
const hstcpsvr_shared_c& cshared;
volatile hstcpsvr_shared_v& vshared;
long worker_id;
dbcontext_ptr dbctx;
hstcpsvr_conns_type conns; /* conns refs dbctx */
time_t last_check_time;
std::vector<pollfd> pfds;
#ifdef __linux__
std::vector<epoll_event> events_vec;
auto_file epoll_fd;
#endif
bool accept_enabled;
int accept_balance;
std::vector<record_filter> filters_work;
private:
int run_one_nb();
int run_one_ep();
void execute_lines(hstcpsvr_conn& conn);
void execute_line(char *start, char *finish, hstcpsvr_conn& conn);
void do_open_index(char *start, char *finish, hstcpsvr_conn& conn);
void do_exec_on_index(char *cmd_begin, char *cmd_end, char *start,
char *finish, hstcpsvr_conn& conn);
void do_authorization(char *start, char *finish, hstcpsvr_conn& conn);
};
hstcpsvr_worker::hstcpsvr_worker(const hstcpsvr_worker_arg& arg)
: cshared(*arg.cshared), vshared(*arg.vshared), worker_id(arg.worker_id),
dbctx(cshared.dbptr->create_context(cshared.for_write_flag)),
last_check_time(time(0)), accept_enabled(true), accept_balance(0)
{
#ifdef __linux__
if (cshared.sockargs.use_epoll) {
epoll_fd.reset(epoll_create(10));
if (epoll_fd.get() < 0) {
fatal_abort("epoll_create");
}
epoll_event ev;
memset(&ev, 0, sizeof(ev));
ev.events = EPOLLIN;
ev.data.ptr = 0;
if (epoll_ctl(epoll_fd.get(), EPOLL_CTL_ADD, cshared.listen_fd.get(), &ev)
!= 0) {
fatal_abort("epoll_ctl EPOLL_CTL_ADD");
}
events_vec.resize(10240);
}
#endif
accept_balance = cshared.conf.get_int("accept_balance", 0);
}
namespace {
struct thr_init {
thr_init(const dbcontext_ptr& dc, volatile int& shutdown_flag) : dbctx(dc) {
dbctx->init_thread(this, shutdown_flag);
}
~thr_init() {
dbctx->term_thread();
}
const dbcontext_ptr& dbctx;
};
}; // namespace
void
hstcpsvr_worker::run()
{
thr_init initobj(dbctx, vshared.shutdown);
#ifdef __linux__
if (cshared.sockargs.use_epoll) {
while (!vshared.shutdown && dbctx->check_alive()) {
run_one_ep();
}
} else if (cshared.sockargs.nonblocking) {
while (!vshared.shutdown && dbctx->check_alive()) {
run_one_nb();
}
} else {
/* UNUSED */
fatal_abort("run_one");
}
#else
while (!vshared.shutdown && dbctx->check_alive()) {
run_one_nb();
}
#endif
}
int
hstcpsvr_worker::run_one_nb()
{
size_t nfds = 0;
/* CLIENT SOCKETS */
for (hstcpsvr_conns_type::const_iterator i = conns.begin();
i != conns.end(); ++i) {
if (pfds.size() <= nfds) {
pfds.resize(nfds + 1);
}
pollfd& pfd = pfds[nfds++];
pfd.fd = (*i)->fd.get();
short ev = 0;
if ((*i)->cstate.writebuf.size() != 0) {
ev = POLLOUT;
} else {
ev = POLLIN;
}
pfd.events = pfd.revents = ev;
}
/* LISTENER */
{
const size_t cpt = cshared.nb_conn_per_thread;
const short ev = (cpt > nfds) ? POLLIN : 0;
if (pfds.size() <= nfds) {
pfds.resize(nfds + 1);
}
pollfd& pfd = pfds[nfds++];
pfd.fd = cshared.listen_fd.get();
pfd.events = pfd.revents = ev;
}
/* POLL */
const int npollev = poll(&pfds[0], nfds, 1 * 1000);
dbctx->set_statistics(conns.size(), npollev);
const time_t now = time(0);
size_t j = 0;
const short mask_in = ~POLLOUT;
const short mask_out = POLLOUT | POLLERR | POLLHUP | POLLNVAL;
/* READ */
for (hstcpsvr_conns_type::iterator i = conns.begin(); i != conns.end();
++i, ++j) {
pollfd& pfd = pfds[j];
if ((pfd.revents & mask_in) == 0) {
continue;
}
hstcpsvr_conn& conn = **i;
if (conn.read_more()) {
if (conn.cstate.readbuf.size() > 0) {
const char ch = conn.cstate.readbuf.begin()[0];
if (ch == 'Q') {
vshared.shutdown = 1;
} else if (ch == '/') {
conn.cstate.readbuf.clear();
conn.cstate.writebuf.clear();
conn.read_finished = true;
conn.write_finished = true;
}
}
conn.nb_last_io = now;
}
}
/* EXECUTE */
j = 0;
for (hstcpsvr_conns_type::iterator i = conns.begin(); i != conns.end();
++i, ++j) {
pollfd& pfd = pfds[j];
if ((pfd.revents & mask_in) == 0 || (*i)->cstate.readbuf.size() == 0) {
continue;
}
execute_lines(**i);
}
/* COMMIT */
dbctx->unlock_tables_if();
const bool commit_error = dbctx->get_commit_error();
dbctx->clear_error();
/* WRITE/CLOSE */
j = 0;
for (hstcpsvr_conns_type::iterator i = conns.begin(); i != conns.end();
++j) {
pollfd& pfd = pfds[j];
hstcpsvr_conn& conn = **i;
hstcpsvr_conns_type::iterator icur = i;
++i;
if (commit_error) {
conn.reset();
continue;
}
if ((pfd.revents & (mask_out | mask_in)) != 0) {
if (conn.write_more()) {
conn.nb_last_io = now;
}
}
if (cshared.sockargs.timeout != 0 &&
conn.nb_last_io + cshared.sockargs.timeout < now) {
conn.reset();
}
if (conn.closed() || conn.ok_to_close()) {
conns.erase_ptr(icur);
}
}
/* ACCEPT */
{
pollfd& pfd = pfds[nfds - 1];
if ((pfd.revents & mask_in) != 0) {
std::auto_ptr<hstcpsvr_conn> c(new hstcpsvr_conn());
c->nonblocking = true;
c->readsize = cshared.readsize;
c->accept(cshared);
if (c->fd.get() >= 0) {
if (fcntl(c->fd.get(), F_SETFL, O_NONBLOCK) != 0) {
fatal_abort("F_SETFL O_NONBLOCK");
}
c->nb_last_io = now;
conns.push_back_ptr(c);
} else {
/* errno == 11 (EAGAIN) is not a fatal error. */
DENA_VERBOSE(100, fprintf(stderr,
"accept failed: errno=%d (not fatal)\n", errno));
}
}
}
DENA_VERBOSE(30, fprintf(stderr, "nb: %p nfds=%zu cns=%zu\n", this, nfds,
conns.size()));
if (conns.empty()) {
dbctx->close_tables_if();
}
dbctx->set_statistics(conns.size(), 0);
return 0;
}
#ifdef __linux__
int
hstcpsvr_worker::run_one_ep()
{
epoll_event *const events = &events_vec[0];
const size_t num_events = events_vec.size();
const time_t now = time(0);
size_t in_count = 0, out_count = 0, accept_count = 0;
int nfds = epoll_wait(epoll_fd.get(), events, num_events, 1000);
/* READ/ACCEPT */
dbctx->set_statistics(conns.size(), nfds);
for (int i = 0; i < nfds; ++i) {
epoll_event& ev = events[i];
if ((ev.events & EPOLLIN) == 0) {
continue;
}
hstcpsvr_conn *const conn = static_cast<hstcpsvr_conn *>(ev.data.ptr);
if (conn == 0) {
/* listener */
++accept_count;
DBG_EP(fprintf(stderr, "IN listener\n"));
std::auto_ptr<hstcpsvr_conn> c(new hstcpsvr_conn());
c->nonblocking = true;
c->readsize = cshared.readsize;
c->accept(cshared);
if (c->fd.get() >= 0) {
if (fcntl(c->fd.get(), F_SETFL, O_NONBLOCK) != 0) {
fatal_abort("F_SETFL O_NONBLOCK");
}
epoll_event cev;
memset(&cev, 0, sizeof(cev));
cev.events = EPOLLIN | EPOLLOUT | EPOLLET;
cev.data.ptr = c.get();
c->nb_last_io = now;
const int fd = c->fd.get();
conns.push_back_ptr(c);
conns.back()->conns_iter = --conns.end();
if (epoll_ctl(epoll_fd.get(), EPOLL_CTL_ADD, fd, &cev) != 0) {
fatal_abort("epoll_ctl EPOLL_CTL_ADD");
}
} else {
DENA_VERBOSE(100, fprintf(stderr,
"accept failed: errno=%d (not fatal)\n", errno));
}
} else {
/* client connection */
++in_count;
DBG_EP(fprintf(stderr, "IN client\n"));
bool more_data = false;
while (conn->read_more(&more_data)) {
DBG_EP(fprintf(stderr, "IN client read_more\n"));
conn->nb_last_io = now;
if (!more_data) {
break;
}
}
}
}
/* EXECUTE */
for (int i = 0; i < nfds; ++i) {
epoll_event& ev = events[i];
hstcpsvr_conn *const conn = static_cast<hstcpsvr_conn *>(ev.data.ptr);
if ((ev.events & EPOLLIN) == 0 || conn == 0 ||
conn->cstate.readbuf.size() == 0) {
continue;
}
const char ch = conn->cstate.readbuf.begin()[0];
if (ch == 'Q') {
vshared.shutdown = 1;
} else if (ch == '/') {
conn->cstate.readbuf.clear();
conn->cstate.writebuf.clear();
conn->read_finished = true;
conn->write_finished = true;
} else {
execute_lines(*conn);
}
}
/* COMMIT */
dbctx->unlock_tables_if();
const bool commit_error = dbctx->get_commit_error();
dbctx->clear_error();
/* WRITE */
for (int i = 0; i < nfds; ++i) {
epoll_event& ev = events[i];
hstcpsvr_conn *const conn = static_cast<hstcpsvr_conn *>(ev.data.ptr);
if (commit_error && conn != 0) {
conn->reset();
continue;
}
if ((ev.events & EPOLLOUT) == 0) {
continue;
}
++out_count;
if (conn == 0) {
/* listener */
DBG_EP(fprintf(stderr, "OUT listener\n"));
} else {
/* client connection */
DBG_EP(fprintf(stderr, "OUT client\n"));
bool more_data = false;
while (conn->write_more(&more_data)) {
DBG_EP(fprintf(stderr, "OUT client write_more\n"));
conn->nb_last_io = now;
if (!more_data) {
break;
}
}
}
}
/* CLOSE */
for (int i = 0; i < nfds; ++i) {
epoll_event& ev = events[i];
hstcpsvr_conn *const conn = static_cast<hstcpsvr_conn *>(ev.data.ptr);
if (conn != 0 && conn->ok_to_close()) {
DBG_EP(fprintf(stderr, "CLOSE close\n"));
conns.erase_ptr(conn->conns_iter);
}
}
/* TIMEOUT & cleanup */
if (last_check_time + 10 < now) {
for (hstcpsvr_conns_type::iterator i = conns.begin();
i != conns.end(); ) {
hstcpsvr_conns_type::iterator icur = i;
++i;
if (cshared.sockargs.timeout != 0 &&
(*icur)->nb_last_io + cshared.sockargs.timeout < now) {
conns.erase_ptr((*icur)->conns_iter);
}
}
last_check_time = now;
DENA_VERBOSE(20, fprintf(stderr, "ep: %p nfds=%d cns=%zu\n", this, nfds,
conns.size()));
}
DENA_VERBOSE(30, fprintf(stderr, "%p in=%zu out=%zu ac=%zu, cns=%zu\n",
this, in_count, out_count, accept_count, conns.size()));
if (conns.empty()) {
dbctx->close_tables_if();
}
/* STATISTICS */
const size_t num_conns = conns.size();
dbctx->set_statistics(num_conns, 0);
/* ENABLE/DISABLE ACCEPT */
if (accept_balance != 0) {
cshared.thread_num_conns[worker_id] = num_conns;
size_t total_num_conns = 0;
for (long i = 0; i < cshared.num_threads; ++i) {
total_num_conns += cshared.thread_num_conns[i];
}
bool e_acc = false;
if (num_conns < 10 ||
total_num_conns * 2 > num_conns * cshared.num_threads) {
e_acc = true;
}
epoll_event ev;
memset(&ev, 0, sizeof(ev));
ev.events = EPOLLIN;
ev.data.ptr = 0;
if (e_acc == accept_enabled) {
} else if (e_acc) {
if (epoll_ctl(epoll_fd.get(), EPOLL_CTL_ADD, cshared.listen_fd.get(), &ev)
!= 0) {
fatal_abort("epoll_ctl EPOLL_CTL_ADD");
}
} else {
if (epoll_ctl(epoll_fd.get(), EPOLL_CTL_DEL, cshared.listen_fd.get(), &ev)
!= 0) {
fatal_abort("epoll_ctl EPOLL_CTL_ADD");
}
}
accept_enabled = e_acc;
}
return 0;
}
#endif
void
hstcpsvr_worker::execute_lines(hstcpsvr_conn& conn)
{
dbconnstate& cstate = conn.cstate;
char *buf_end = cstate.readbuf.end();
char *line_begin = cstate.readbuf.begin();
while (true) {
char *const nl = memchr_char(line_begin, '\n', buf_end - line_begin);
if (nl == 0) {
break;
}
char *const lf = (line_begin != nl && nl[-1] == '\r') ? nl - 1 : nl;
execute_line(line_begin, lf, conn);
line_begin = nl + 1;
}
cstate.readbuf.erase_front(line_begin - cstate.readbuf.begin());
}
void
hstcpsvr_worker::execute_line(char *start, char *finish, hstcpsvr_conn& conn)
{
/* safe to modify, safe to dereference 'finish' */
char *const cmd_begin = start;
read_token(start, finish);
char *const cmd_end = start;
skip_one(start, finish);
if (cmd_begin == cmd_end) {
return conn.dbcb_resp_short(2, "cmd");
}
if (cmd_begin + 1 == cmd_end) {
if (cmd_begin[0] == 'P') {
if (cshared.require_auth && !conn.authorized) {
return conn.dbcb_resp_short(3, "unauth");
}
return do_open_index(start, finish, conn);
}
if (cmd_begin[0] == 'A') {
return do_authorization(start, finish, conn);
}
}
if (cmd_begin[0] >= '0' && cmd_begin[0] <= '9') {
if (cshared.require_auth && !conn.authorized) {
return conn.dbcb_resp_short(3, "unauth");
}
return do_exec_on_index(cmd_begin, cmd_end, start, finish, conn);
}
return conn.dbcb_resp_short(2, "cmd");
}
void
hstcpsvr_worker::do_open_index(char *start, char *finish, hstcpsvr_conn& conn)
{
const size_t pst_id = read_ui32(start, finish);
skip_one(start, finish);
/* dbname */
char *const dbname_begin = start;
read_token(start, finish);
char *const dbname_end = start;
skip_one(start, finish);
/* tblname */
char *const tblname_begin = start;
read_token(start, finish);
char *const tblname_end = start;
skip_one(start, finish);
/* idxname */
char *const idxname_begin = start;
read_token(start, finish);
char *const idxname_end = start;
skip_one(start, finish);
/* retfields */
char *const retflds_begin = start;
read_token(start, finish);
char *const retflds_end = start;
skip_one(start, finish);
/* filfields */
char *const filflds_begin = start;
read_token(start, finish);
char *const filflds_end = start;
dbname_end[0] = 0;
tblname_end[0] = 0;
idxname_end[0] = 0;
retflds_end[0] = 0;
filflds_end[0] = 0;
return dbctx->cmd_open_index(conn, pst_id, dbname_begin, tblname_begin,
idxname_begin, retflds_begin, filflds_begin);
}
void
hstcpsvr_worker::do_exec_on_index(char *cmd_begin, char *cmd_end, char *start,
char *finish, hstcpsvr_conn& conn)
{
cmd_exec_args args;
const size_t pst_id = read_ui32(cmd_begin, cmd_end);
if (pst_id >= conn.cstate.prep_stmts.size()) {
return conn.dbcb_resp_short(2, "stmtnum");
}
args.pst = &conn.cstate.prep_stmts[pst_id];
char *const op_begin = start;
read_token(start, finish);
char *const op_end = start;
args.op = string_ref(op_begin, op_end);
skip_one(start, finish);
const uint32_t fldnum = read_ui32(start, finish);
string_ref flds[fldnum]; /* GNU */
args.kvals = flds;
args.kvalslen = fldnum;
for (size_t i = 0; i < fldnum; ++i) {
skip_one(start, finish);
char *const f_begin = start;
read_token(start, finish);
char *const f_end = start;
if (is_null_expression(f_begin, f_end)) {
/* null */
flds[i] = string_ref();
} else {
/* non-null */
char *wp = f_begin;
unescape_string(wp, f_begin, f_end);
flds[i] = string_ref(f_begin, wp - f_begin);
}
}
skip_one(start, finish);
args.limit = read_ui32(start, finish);
skip_one(start, finish);
args.skip = read_ui32(start, finish);
if (start == finish) {
/* simple query */
return dbctx->cmd_exec_on_index(conn, args);
}
/* has filters or modops */
skip_one(start, finish);
/* filters */
size_t filters_count = 0;
while (start != finish && (start[0] == 'W' || start[0] == 'F')) {
char *const filter_type_begin = start;
read_token(start, finish);
char *const filter_type_end = start;
skip_one(start, finish);
char *const filter_op_begin = start;
read_token(start, finish);
char *const filter_op_end = start;
skip_one(start, finish);
const uint32_t ff_offset = read_ui32(start, finish);
skip_one(start, finish);
char *const filter_val_begin = start;
read_token(start, finish);
char *const filter_val_end = start;
skip_one(start, finish);
if (filters_work.size() <= filters_count) {
filters_work.resize(filters_count + 1);
}
record_filter& fi = filters_work[filters_count];
if (filter_type_end != filter_type_begin + 1) {
return conn.dbcb_resp_short(2, "filtertype");
}
fi.filter_type = (filter_type_begin[0] == 'W')
? record_filter_type_break : record_filter_type_skip;
const uint32_t num_filflds = args.pst->get_filter_fields().size();
if (ff_offset >= num_filflds) {
return conn.dbcb_resp_short(2, "filterfld");
}
fi.op = string_ref(filter_op_begin, filter_op_end);
fi.ff_offset = ff_offset;
if (is_null_expression(filter_val_begin, filter_val_end)) {
/* null */
fi.val = string_ref();
} else {
/* non-null */
char *wp = filter_val_begin;
unescape_string(wp, filter_val_begin, filter_val_end);
fi.val = string_ref(filter_val_begin, wp - filter_val_begin);
}
++filters_count;
}
if (filters_count > 0) {
if (filters_work.size() <= filters_count) {
filters_work.resize(filters_count + 1);
}
filters_work[filters_count].op = string_ref(); /* sentinel */
args.filters = &filters_work[0];
} else {
args.filters = 0;
}
if (start == finish) {
/* no modops */
return dbctx->cmd_exec_on_index(conn, args);
}
/* has modops */
char *const mod_op_begin = start;
read_token(start, finish);
char *const mod_op_end = start;
args.mod_op = string_ref(mod_op_begin, mod_op_end);
const size_t num_uvals = args.pst->get_ret_fields().size();
string_ref uflds[num_uvals]; /* GNU */
for (size_t i = 0; i < num_uvals; ++i) {
skip_one(start, finish);
char *const f_begin = start;
read_token(start, finish);
char *const f_end = start;
if (is_null_expression(f_begin, f_end)) {
/* null */
uflds[i] = string_ref();
} else {
/* non-null */
char *wp = f_begin;
unescape_string(wp, f_begin, f_end);
uflds[i] = string_ref(f_begin, wp - f_begin);
}
}
args.uvals = uflds;
return dbctx->cmd_exec_on_index(conn, args);
}
void
hstcpsvr_worker::do_authorization(char *start, char *finish,
hstcpsvr_conn& conn)
{
/* auth type */
char *const authtype_begin = start;
read_token(start, finish);
char *const authtype_end = start;
const size_t authtype_len = authtype_end - authtype_begin;
skip_one(start, finish);
/* key */
char *const key_begin = start;
read_token(start, finish);
char *const key_end = start;
const size_t key_len = key_end - key_begin;
authtype_end[0] = 0;
key_end[0] = 0;
char *wp = key_begin;
unescape_string(wp, key_begin, key_end);
if (authtype_len != 1 || authtype_begin[0] != '1') {
return conn.dbcb_resp_short(2, "authtype");
}
if (cshared.plain_secret.size() == key_len &&
memcmp(cshared.plain_secret.data(), key_begin, key_len) == 0) {
conn.authorized = true;
} else {
conn.authorized = false;
}
if (!conn.authorized) {
return conn.dbcb_resp_short(3, "unauth");
} else {
return conn.dbcb_resp_short(0, "");
}
}
hstcpsvr_worker_ptr
hstcpsvr_worker_i::create(const hstcpsvr_worker_arg& arg)
{
return hstcpsvr_worker_ptr(new hstcpsvr_worker(arg));
}
};
// vim:sw=2:ai
/*
* Copyright (C) 2010 DeNA Co.,Ltd.. All rights reserved.
* See COPYRIGHT.txt for details.
*/
#ifndef DENA_HSTCPSVR_WORKER_HPP
#define DENA_HSTCPSVR_WORKER_HPP
#include "hstcpsvr.hpp"
namespace dena {
struct hstcpsvr_worker_i;
typedef std::auto_ptr<hstcpsvr_worker_i> hstcpsvr_worker_ptr;
struct hstcpsvr_worker_arg {
const hstcpsvr_shared_c *cshared;
volatile hstcpsvr_shared_v *vshared;
long worker_id;
hstcpsvr_worker_arg() : cshared(0), vshared(0), worker_id(0) { }
};
struct hstcpsvr_worker_i {
virtual ~hstcpsvr_worker_i() { }
virtual void run() = 0;
static hstcpsvr_worker_ptr create(const hstcpsvr_worker_arg& arg);
};
};
#endif
// vim:sw=2:ai
/*
* Copyright (C) 2010 DeNA Co.,Ltd.. All rights reserved.
* See COPYRIGHT.txt for details.
*/
#ifndef DENA_MYSQL_INCL_HPP
#define DENA_MYSQL_INCL_HPP
#ifndef HAVE_CONFIG_H
#define HAVE_CONFIG_H
#endif
#define MYSQL_DYNAMIC_PLUGIN
#define MYSQL_SERVER 1
#include <my_config.h>
#include <mysql_version.h>
#if MYSQL_VERSION_ID >= 50505
#include <my_pthread.h>
#include <sql_priv.h>
#include "sql_class.h"
#include "unireg.h"
#include "lock.h"
#include "key.h" // key_copy()
#include <my_global.h>
#include <mysql/plugin.h>
#include <transaction.h>
#include <sql_base.h>
// FIXME FIXME FIXME
#define safeFree(X) my_free(X)
#define pthread_cond_timedwait mysql_cond_timedwait
#define pthread_mutex_lock mysql_mutex_lock
#define pthread_mutex_unlock mysql_mutex_unlock
#define current_stmt_binlog_row_based is_current_stmt_binlog_format_row
#define clear_current_stmt_binlog_row_based clear_current_stmt_binlog_format_row
#else
#include "mysql_priv.h"
#endif
#undef min
#undef max
#endif
Copyright (c) 2010 DeNA Co.,Ltd.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of DeNA Co.,Ltd. nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY DeNA Co.,Ltd. "AS IS" AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
EVENT SHALL DeNA Co.,Ltd. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
CXXFLAGS += -fimplicit-templates
instdir = $(includedir)/handlersocket
# TODO: these headers should be in dena/
inst_HEADERS = allocator.hpp config.hpp mutex.hpp string_util.hpp \
auto_addrinfo.hpp escape.hpp socket.hpp thread.hpp auto_file.hpp \
fatal.hpp string_buffer.hpp util.hpp auto_ptrcontainer.hpp \
hstcpcli.hpp string_ref.hpp
lib_LTLIBRARIES = libhsclient.la
libhsclient_la_SOURCES = config.cpp escape.cpp fatal.cpp hstcpcli.cpp \
socket.cpp string_util.cpp
#libhsclient_la_LDFLAGS = -static
libhsclient_la_CFLAGS = $(AM_CFLAGS)
libhsclient_la_CXXFLAGS = $(AM_CFLAGS)
CXX = g++ -Wall -g -fno-rtti -fno-exceptions -fPIC -DPIC
LDFLAGS =
CXXFLAGS += -O3 -DNDEBUG
COMMON_OBJS = config.o fatal.o socket.o string_util.o escape.o
HSCLIENT_OBJS = $(COMMON_OBJS) hstcpcli.o
all: libhsclient.a
libhsclient.a: $(HSCLIENT_OBJS)
$(AR) rc $@ $^
$(AR) s $@
clean:
rm -f *.a *.so *.o
LIBDIR = $(shell \
if [ -e /usr/lib64/mysql ]; then echo /usr/lib64; else echo /usr/lib; fi)
install: libhsclient.a
sudo sh -c 'cp libhsclient.a libhsclient.a.cpy && \
mv libhsclient.a.cpy $(LIBDIR)/libhsclient.a && \
mkdir -p /usr/include/handlersocket && \
cp -a *.hpp /usr/include/handlersocket/'
// vim:sw=2:ai
/*
* Copyright (C) 2010 DeNA Co.,Ltd.. All rights reserved.
* See COPYRIGHT.txt for details.
*/
#ifndef DENA_ALLOCATOR_HPP
#define DENA_ALLOCATOR_HPP
#include <stdlib.h>
#include <string.h>
#if 0
extern "C" {
#include <tlsf.h>
};
#define DENA_MALLOC(x) tlsf_malloc(x)
#define DENA_REALLOC(x, y) tlsf_realloc(x, y)
#define DENA_FREE(x) tlsf_free(x)
#define DENA_NEWCHAR(x) static_cast<char *>(tlsf_malloc(x))
#define DENA_DELETE(x) tlsf_free(x)
typedef std::allocator<int> allocator_type;
#endif
#if 1
#define DENA_MALLOC(x) malloc(x)
#define DENA_REALLOC(x, y) realloc(x, y)
#define DENA_FREE(x) free(x)
#define DENA_NEWCHAR(x) (new char[x])
#define DENA_DELETE(x) (delete [] x)
typedef std::allocator<int> allocator_type;
#endif
#endif
// vim:sw=2:ai
/*
* Copyright (C) 2010 DeNA Co.,Ltd.. All rights reserved.
* See COPYRIGHT.txt for details.
*/
#ifndef DENA_AUTO_ADDRINFO_HPP
#define DENA_AUTO_ADDRINFO_HPP
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <string.h>
#include "util.hpp"
namespace dena {
struct auto_addrinfo : private noncopyable {
auto_addrinfo() : addr(0) { }
~auto_addrinfo() {
reset();
}
void reset(addrinfo *a = 0) {
if (addr != 0) {
freeaddrinfo(addr);
}
addr = a;
}
const addrinfo *get() const { return addr; }
int resolve(const char *node, const char *service, int flags = 0,
int family = AF_UNSPEC, int socktype = SOCK_STREAM, int protocol = 0) {
reset();
addrinfo hints;
memset(&hints, 0, sizeof(hints));
hints.ai_flags = flags;
hints.ai_family = family;
hints.ai_socktype = socktype;
hints.ai_protocol = protocol;
return getaddrinfo(node, service, &hints, &addr);
}
private:
addrinfo *addr;
};
};
#endif
// vim:sw=2:ai
/*
* Copyright (C) 2010 DeNA Co.,Ltd.. All rights reserved.
* See COPYRIGHT.txt for details.
*/
#ifndef DENA_AUTO_FILE_HPP
#define DENA_AUTO_FILE_HPP
#include <unistd.h>
#include <sys/types.h>
#include <dirent.h>
#include <stdio.h>
#include "util.hpp"
namespace dena {
struct auto_file : private noncopyable {
auto_file() : fd(-1) { }
~auto_file() {
reset();
}
int get() const { return fd; }
int close() {
if (fd < 0) {
return 0;
}
const int r = ::close(fd);
fd = -1;
return r;
}
void reset(int x = -1) {
if (fd >= 0) {
this->close();
}
fd = x;
}
private:
int fd;
};
struct auto_dir : private noncopyable {
auto_dir() : dp(0) { }
~auto_dir() {
reset();
}
DIR *get() const { return dp; }
void reset(DIR *d = 0) {
if (dp != 0) {
closedir(dp);
}
dp = d;
}
private:
DIR *dp;
};
};
#endif
// vim:sw=2:ai
/*
* Copyright (C) 2010 DeNA Co.,Ltd.. All rights reserved.
* See COPYRIGHT.txt for details.
*/
#ifndef DENA_AUTO_PTRCONTAINER_HPP
#define DENA_AUTO_PTRCONTAINER_HPP
namespace dena {
template <typename Tcnt>
struct auto_ptrcontainer {
typedef Tcnt container_type;
typedef typename container_type::value_type value_type;
typedef typename container_type::pointer pointer;
typedef typename container_type::reference reference;
typedef typename container_type::const_reference const_reference;
typedef typename container_type::size_type size_type;
typedef typename container_type::difference_type difference_type;
typedef typename container_type::iterator iterator;
typedef typename container_type::const_iterator const_iterator;
typedef typename container_type::reverse_iterator reverse_iterator;
typedef typename container_type::const_reverse_iterator
const_reverse_iterator;
iterator begin() { return cnt.begin(); }
const_iterator begin() const { return cnt.begin(); }
iterator end() { return cnt.end(); }
const_iterator end() const { return cnt.end(); }
reverse_iterator rbegin() { return cnt.rbegin(); }
reverse_iterator rend() { return cnt.rend(); }
const_reverse_iterator rbegin() const { return cnt.rbegin(); }
const_reverse_iterator rend() const { return cnt.rend(); }
size_type size() const { return cnt.size(); }
size_type max_size() const { return cnt.max_size(); }
bool empty() const { return cnt.empty(); }
reference front() { return cnt.front(); }
const_reference front() const { cnt.front(); }
reference back() { return cnt.back(); }
const_reference back() const { cnt.back(); }
void swap(auto_ptrcontainer& x) { cnt.swap(x.cnt); }
~auto_ptrcontainer() {
for (iterator i = begin(); i != end(); ++i) {
delete *i;
}
}
template <typename Tap> void push_back_ptr(Tap& ap) {
cnt.push_back(ap.get());
ap.release();
}
void erase_ptr(iterator i) {
delete *i;
cnt.erase(i);
}
reference operator [](size_type n) { return cnt[n]; }
const_reference operator [](size_type n) const { return cnt[n]; }
void clear() { cnt.clear(); }
private:
Tcnt cnt;
};
};
#endif
// vim:sw=2:ai
/*
* Copyright (C) 2010 DeNA Co.,Ltd.. All rights reserved.
* See COPYRIGHT.txt for details.
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "config.hpp"
namespace dena {
unsigned int verbose_level = 0;
std::string
config::get_str(const std::string& key, const std::string& def) const
{
const_iterator iter = this->find(key);
if (iter == this->end()) {
DENA_VERBOSE(10, fprintf(stderr, "CONFIG: %s=%s(default)\n", key.c_str(),
def.c_str()));
return def;
}
DENA_VERBOSE(10, fprintf(stderr, "CONFIG: %s=%s\n", key.c_str(),
iter->second.c_str()));
return iter->second;
}
long long
config::get_int(const std::string& key, long long def) const
{
const_iterator iter = this->find(key);
if (iter == this->end()) {
DENA_VERBOSE(10, fprintf(stderr, "CONFIG: %s=%lld(default)\n", key.c_str(),
def));
return def;
}
const long long r = atoll(iter->second.c_str());
DENA_VERBOSE(10, fprintf(stderr, "CONFIG: %s=%lld\n", key.c_str(), r));
return r;
}
void
parse_args(int argc, char **argv, config& conf)
{
for (int i = 1; i < argc; ++i) {
const char *const arg = argv[i];
const char *const eq = strchr(arg, '=');
if (eq == 0) {
continue;
}
const std::string key(arg, eq - arg);
const std::string val(eq + 1);
conf[key] = val;
}
config::const_iterator iter = conf.find("verbose");
if (iter != conf.end()) {
verbose_level = atoi(iter->second.c_str());
}
}
};
// vim:sw=2:ai
/*
* Copyright (C) 2010 DeNA Co.,Ltd.. All rights reserved.
* See COPYRIGHT.txt for details.
*/
#ifndef DENA_CONFIG_HPP
#define DENA_CONFIG_HPP
#include <string>
#include <map>
#define DENA_VERBOSE(lv, x) if (dena::verbose_level >= (lv)) { (x); }
namespace dena {
struct config : public std::map<std::string, std::string> {
std::string get_str(const std::string& key, const std::string& def = "")
const;
long long get_int(const std::string& key, long long def = 0) const;
};
void parse_args(int argc, char **argv, config& conf);
extern unsigned int verbose_level;
};
#endif
// vim:sw=2:ai
/*
* Copyright (C) 2010 DeNA Co.,Ltd.. All rights reserved.
* See COPYRIGHT.txt for details.
*/
#include <stdio.h>
#include "escape.hpp"
#include "string_buffer.hpp"
#include "fatal.hpp"
#include "string_util.hpp"
#define DBG_OP(x)
#define DBG_BUF(x)
namespace dena {
enum special_char_t {
special_char_escape_prefix = 0x01, /* SOH */
special_char_noescape_min = 0x10, /* DLE */
special_char_escape_shift = 0x40, /* '@' */
};
void
escape_string(char *& wp, const char *start, const char *finish)
{
while (start != finish) {
const unsigned char c = *start;
if (c >= special_char_noescape_min) {
wp[0] = c; /* no need to escape */
} else {
wp[0] = special_char_escape_prefix;
++wp;
wp[0] = c + special_char_escape_shift;
}
++start;
++wp;
}
}
void
escape_string(string_buffer& ar, const char *start, const char *finish)
{
const size_t buflen = (finish - start) * 2;
char *const wp_begin = ar.make_space(buflen);
char *wp = wp_begin;
escape_string(wp, start, finish);
ar.space_wrote(wp - wp_begin);
}
bool
unescape_string(char *& wp, const char *start, const char *finish)
{
/* works even if wp == start */
while (start != finish) {
const unsigned char c = *start;
if (c != special_char_escape_prefix) {
wp[0] = c;
} else if (start + 1 != finish) {
++start;
const unsigned char cn = *start;
if (cn < special_char_escape_shift) {
return false;
}
wp[0] = cn - special_char_escape_shift;
} else {
return false;
}
++start;
++wp;
}
return true;
}
bool
unescape_string(string_buffer& ar, const char *start, const char *finish)
{
const size_t buflen = finish - start;
char *const wp_begin = ar.make_space(buflen);
char *wp = wp_begin;
const bool r = unescape_string(wp, start, finish);
ar.space_wrote(wp - wp_begin);
return r;
}
uint32_t
read_ui32(char *& start, char *finish)
{
char *const n_begin = start;
read_token(start, finish);
char *const n_end = start;
uint32_t v = 0;
for (char *p = n_begin; p != n_end; ++p) {
const char ch = p[0];
if (ch >= '0' && ch <= '9') {
v *= 10;
v += (ch - '0');
}
}
return v;
}
void
write_ui32(string_buffer& buf, uint32_t v)
{
char *wp = buf.make_space(32);
int len = snprintf(wp, 32, "%u", v);
if (len > 0) {
buf.space_wrote(len);
}
}
};
// vim:sw=2:ai
/*
* Copyright (C) 2010 DeNA Co.,Ltd.. All rights reserved.
* See COPYRIGHT.txt for details.
*/
#include <stdint.h>
#include "string_buffer.hpp"
#include "string_ref.hpp"
#include "string_util.hpp"
#ifndef DENA_ESCAPE_HPP
#define DENA_ESCAPE_HPP
namespace dena {
void escape_string(char *& wp, const char *start, const char *finish);
void escape_string(string_buffer& ar, const char *start, const char *finish);
bool unescape_string(char *& wp, const char *start, const char *finish);
/* unescaped_string() works even if wp == start */
bool unescape_string(string_buffer& ar, const char *start, const char *finish);
uint32_t read_ui32(char *& start, char *finish);
void write_ui32(string_buffer& buf, uint32_t v);
inline bool
is_null_expression(const char *start, const char *finish)
{
return (finish == start + 1 && start[0] == 0);
}
inline void
read_token(char *& start, char *finish)
{
char *const p = memchr_char(start, '\t', finish - start);
if (p == 0) {
start = finish;
} else {
start = p;
}
}
inline void
skip_token_delim_fold(char *& start, char *finish)
{
while (start != finish && start[0] == '\t') {
++start;
}
}
inline void
skip_one(char *& start, char *finish)
{
if (start != finish) {
++start;
}
}
};
#endif
// vim:sw=2:ai
/*
* Copyright (C) 2010 DeNA Co.,Ltd.. All rights reserved.
* See COPYRIGHT.txt for details.
*/
#include <stdlib.h>
#include <stdio.h>
#include <syslog.h>
#include "fatal.hpp"
namespace dena {
const int opt_syslog = LOG_ERR | LOG_PID | LOG_CONS;
void
fatal_exit(const std::string& message)
{
fprintf(stderr, "FATAL_EXIT: %s\n", message.c_str());
syslog(opt_syslog, "FATAL_EXIT: %s", message.c_str());
_exit(1);
}
void
fatal_abort(const std::string& message)
{
fprintf(stderr, "FATAL_COREDUMP: %s\n", message.c_str());
syslog(opt_syslog, "FATAL_COREDUMP: %s", message.c_str());
abort();
}
};
// vim:sw=2:ai
/*
* Copyright (C) 2010 DeNA Co.,Ltd.. All rights reserved.
* See COPYRIGHT.txt for details.
*/
#ifndef DENA_FATAL_HPP
#define DENA_FATAL_HPP
#include <string>
namespace dena {
void fatal_exit(const std::string& message);
void fatal_abort(const std::string& message);
};
#endif
// vim:sw=2:ai
/*
* Copyright (C) 2010 DeNA Co.,Ltd.. All rights reserved.
* See COPYRIGHT.txt for details.
*/
#include <stdexcept>
#include "hstcpcli.hpp"
#include "auto_file.hpp"
#include "string_util.hpp"
#include "auto_addrinfo.hpp"
#include "escape.hpp"
#include "util.hpp"
/* TODO */
#if !defined(__linux__) && !defined(__FreeBSD__) && !defined(MSG_NOSIGNAL)
#define MSG_NOSIGNAL 0
#endif
#define DBG(x)
namespace dena {
struct hstcpcli : public hstcpcli_i, private noncopyable {
hstcpcli(const socket_args& args);
virtual void close();
virtual int reconnect();
virtual bool stable_point();
virtual void request_buf_open_index(size_t pst_id, const char *dbn,
const char *tbl, const char *idx, const char *retflds, const char *filflds);
#if 0
virtual void request_buf_find(size_t pst_id, const string_ref& op,
const string_ref *kvs, size_t kvslen, uint32_t limit, uint32_t skip);
virtual void request_buf_insert(size_t pst_id, const string_ref *fvs,
size_t fvslen);
virtual void request_buf_update(size_t pst_id, const string_ref& op,
const string_ref *kvs, size_t kvslen, uint32_t limit, uint32_t skip,
const string_ref *mvs, size_t mvslen);
virtual void request_buf_delete(size_t pst_id, const string_ref& op,
const string_ref *kvs, size_t kvslen, uint32_t limit, uint32_t skip);
#endif
virtual void request_buf_exec_generic(size_t pst_id, const string_ref& op,
const string_ref *kvs, size_t kvslen, uint32_t limit, uint32_t skip,
const string_ref& mod_op, const string_ref *mvs, size_t mvslen,
const hstcpcli_filter *fils, size_t filslen);
virtual int request_send();
virtual int response_recv(size_t& num_flds_r);
virtual const string_ref *get_next_row();
virtual void response_buf_remove();
virtual int get_error_code();
virtual std::string get_error();
private:
int read_more();
void clear_error();
int set_error(int code, const std::string& str);
private:
auto_file fd;
socket_args sargs;
string_buffer readbuf;
string_buffer writebuf;
size_t response_end_offset; /* incl newline */
size_t cur_row_offset;
size_t num_flds;
size_t num_req_bufd; /* buffered but not yet sent */
size_t num_req_sent; /* sent but not yet received */
size_t num_req_rcvd; /* received but not yet removed */
int error_code;
std::string error_str;
std::vector<string_ref> flds;
};
hstcpcli::hstcpcli(const socket_args& args)
: sargs(args), response_end_offset(0), cur_row_offset(0), num_flds(0),
num_req_bufd(0), num_req_sent(0), num_req_rcvd(0), error_code(0)
{
std::string err;
if (socket_connect(fd, sargs, err) != 0) {
set_error(-1, err);
}
}
void
hstcpcli::close()
{
fd.close();
readbuf.clear();
writebuf.clear();
flds.clear();
response_end_offset = 0;
cur_row_offset = 0;
num_flds = 0;
num_req_bufd = 0;
num_req_sent = 0;
num_req_rcvd = 0;
}
int
hstcpcli::reconnect()
{
clear_error();
close();
std::string err;
if (socket_connect(fd, sargs, err) != 0) {
set_error(-1, err);
}
return error_code;
}
bool
hstcpcli::stable_point()
{
/* returns true if cli can send a new request */
return fd.get() >= 0 && num_req_bufd == 0 && num_req_sent == 0 &&
num_req_rcvd == 0 && response_end_offset == 0;
}
int
hstcpcli::get_error_code()
{
return error_code;
}
std::string
hstcpcli::get_error()
{
return error_str;
}
int
hstcpcli::read_more()
{
const size_t block_size = 4096; // FIXME
char *const wp = readbuf.make_space(block_size);
const ssize_t rlen = read(fd.get(), wp, block_size);
if (rlen <= 0) {
if (rlen < 0) {
error_str = "read: failed";
} else {
error_str = "read: eof";
}
return rlen;
}
readbuf.space_wrote(rlen);
return rlen;
}
void
hstcpcli::clear_error()
{
DBG(fprintf(stderr, "CLEAR_ERROR: %d\n", error_code));
error_code = 0;
error_str.clear();
}
int
hstcpcli::set_error(int code, const std::string& str)
{
DBG(fprintf(stderr, "SET_ERROR: %d\n", code));
error_code = code;
error_str = str;
return error_code;
}
void
hstcpcli::request_buf_open_index(size_t pst_id, const char *dbn,
const char *tbl, const char *idx, const char *retflds, const char *filflds)
{
if (num_req_sent > 0 || num_req_rcvd > 0) {
close();
set_error(-1, "request_buf_open_index: protocol out of sync");
return;
}
const string_ref dbn_ref(dbn, strlen(dbn));
const string_ref tbl_ref(tbl, strlen(tbl));
const string_ref idx_ref(idx, strlen(idx));
const string_ref rfs_ref(retflds, strlen(retflds));
writebuf.append_literal("P\t");
append_uint32(writebuf, pst_id); // FIXME size_t ?
writebuf.append_literal("\t");
writebuf.append(dbn_ref.begin(), dbn_ref.end());
writebuf.append_literal("\t");
writebuf.append(tbl_ref.begin(), tbl_ref.end());
writebuf.append_literal("\t");
writebuf.append(idx_ref.begin(), idx_ref.end());
writebuf.append_literal("\t");
writebuf.append(rfs_ref.begin(), rfs_ref.end());
if (filflds != 0) {
const string_ref fls_ref(filflds, strlen(filflds));
writebuf.append_literal("\t");
writebuf.append(fls_ref.begin(), fls_ref.end());
}
writebuf.append_literal("\n");
++num_req_bufd;
}
namespace {
void
append_delim_value(string_buffer& buf, const char *start, const char *finish)
{
if (start == 0) {
/* null */
const char t[] = "\t\0";
buf.append(t, t + 2);
} else {
/* non-null */
buf.append_literal("\t");
escape_string(buf, start, finish);
}
}
};
void
hstcpcli::request_buf_exec_generic(size_t pst_id, const string_ref& op,
const string_ref *kvs, size_t kvslen, uint32_t limit, uint32_t skip,
const string_ref& mod_op, const string_ref *mvs, size_t mvslen,
const hstcpcli_filter *fils, size_t filslen)
{
if (num_req_sent > 0 || num_req_rcvd > 0) {
close();
set_error(-1, "request_buf_exec_generic: protocol out of sync");
return;
}
append_uint32(writebuf, pst_id); // FIXME size_t ?
writebuf.append_literal("\t");
writebuf.append(op.begin(), op.end());
writebuf.append_literal("\t");
append_uint32(writebuf, kvslen); // FIXME size_t ?
for (size_t i = 0; i < kvslen; ++i) {
const string_ref& kv = kvs[i];
append_delim_value(writebuf, kv.begin(), kv.end());
}
if (limit != 0 || skip != 0 || mod_op.size() != 0 || filslen != 0) {
writebuf.append_literal("\t");
append_uint32(writebuf, limit); // FIXME size_t ?
if (skip != 0 || mod_op.size() != 0 || filslen != 0) {
writebuf.append_literal("\t");
append_uint32(writebuf, skip); // FIXME size_t ?
}
for (size_t i = 0; i < filslen; ++i) {
const hstcpcli_filter& f = fils[i];
writebuf.append_literal("\t");
writebuf.append(f.filter_type.begin(), f.filter_type.end());
writebuf.append_literal("\t");
writebuf.append(f.op.begin(), f.op.end());
writebuf.append_literal("\t");
append_uint32(writebuf, f.ff_offset);
append_delim_value(writebuf, f.val.begin(), f.val.end());
}
if (mod_op.size() != 0) {
writebuf.append_literal("\t");
writebuf.append(mod_op.begin(), mod_op.end());
for (size_t i = 0; i < mvslen; ++i) {
const string_ref& mv = mvs[i];
append_delim_value(writebuf, mv.begin(), mv.end());
}
}
}
writebuf.append_literal("\n");
++num_req_bufd;
}
#if 0
void
hstcpcli::request_buf_find(size_t pst_id, const string_ref& op,
const string_ref *kvs, size_t kvslen, uint32_t limit, uint32_t skip)
{
return request_buf_exec_generic(pst_id, op, kvs, kvslen, limit, skip,
0, 0, 0);
}
void
hstcpcli::request_buf_insert(size_t pst_id, const string_ref *fvs,
size_t fvslen)
{
const string_ref insert_op("+", 1);
return request_buf_exec_generic(pst_id, insert_op, fvs, fvslen,
0, 0, string_ref(), 0, 0);
}
void
hstcpcli::request_buf_update(size_t pst_id, const string_ref& op,
const string_ref *kvs, size_t kvslen, uint32_t limit, uint32_t skip,
const string_ref *mvs, size_t mvslen)
{
const string_ref modop_update("U", 1);
return request_buf_exec_generic(pst_id, op, kvs, kvslen, limit, skip,
modop_update, mvs, mvslen);
}
void
hstcpcli::request_buf_delete(size_t pst_id, const string_ref& op,
const string_ref *kvs, size_t kvslen, uint32_t limit, uint32_t skip)
{
const string_ref modop_delete("D", 1);
return request_buf_exec_generic(pst_id, op, kvs, kvslen, limit, skip,
modop_delete, 0, 0);
}
#endif
int
hstcpcli::request_send()
{
if (error_code < 0) {
return error_code;
}
clear_error();
if (fd.get() < 0) {
close();
return set_error(-1, "write: closed");
}
if (num_req_bufd == 0 || num_req_sent > 0 || num_req_rcvd > 0) {
close();
return set_error(-1, "request_send: protocol out of sync");
}
const size_t wrlen = writebuf.size();
const ssize_t r = send(fd.get(), writebuf.begin(), wrlen, MSG_NOSIGNAL);
if (r <= 0) {
close();
return set_error(-1, r < 0 ? "write: failed" : "write: eof");
}
writebuf.erase_front(r);
if (static_cast<size_t>(r) != wrlen) {
close();
return set_error(-1, "write: incomplete");
}
num_req_sent = num_req_bufd;
num_req_bufd = 0;
DBG(fprintf(stderr, "REQSEND 0\n"));
return 0;
}
int
hstcpcli::response_recv(size_t& num_flds_r)
{
if (error_code < 0) {
return error_code;
}
clear_error();
if (num_req_bufd > 0 || num_req_sent == 0 || num_req_rcvd > 0 ||
response_end_offset != 0) {
close();
return set_error(-1, "response_recv: protocol out of sync");
}
cur_row_offset = 0;
num_flds_r = num_flds = 0;
if (fd.get() < 0) {
return set_error(-1, "read: closed");
}
size_t offset = 0;
while (true) {
const char *const lbegin = readbuf.begin() + offset;
const char *const lend = readbuf.end();
const char *const nl = memchr_char(lbegin, '\n', lend - lbegin);
if (nl != 0) {
offset = (nl + 1) - readbuf.begin();
break;
}
if (read_more() <= 0) {
close();
return set_error(-1, "read: eof");
}
}
response_end_offset = offset;
--num_req_sent;
++num_req_rcvd;
char *start = readbuf.begin();
char *const finish = start + response_end_offset - 1;
const size_t resp_code = read_ui32(start, finish);
skip_one(start, finish);
num_flds_r = num_flds = read_ui32(start, finish);
if (resp_code != 0) {
skip_one(start, finish);
char *const err_begin = start;
read_token(start, finish);
char *const err_end = start;
std::string e = std::string(err_begin, err_end - err_begin);
if (e.empty()) {
e = "unknown_error";
}
return set_error(resp_code, e);
}
cur_row_offset = start - readbuf.begin();
DBG(fprintf(stderr, "[%s] ro=%zu eol=%zu\n",
std::string(readbuf.begin(), readbuf.begin() + response_end_offset)
.c_str(),
cur_row_offset, response_end_offset));
DBG(fprintf(stderr, "RES 0\n"));
return 0;
}
const string_ref *
hstcpcli::get_next_row()
{
if (num_flds == 0) {
DBG(fprintf(stderr, "GNR NF 0\n"));
return 0;
}
if (flds.size() < num_flds) {
flds.resize(num_flds);
}
char *start = readbuf.begin() + cur_row_offset;
char *const finish = readbuf.begin() + response_end_offset - 1;
if (start >= finish) { /* start[0] == nl */
DBG(fprintf(stderr, "GNR FIN 0 %p %p\n", start, finish));
return 0;
}
for (size_t i = 0; i < num_flds; ++i) {
skip_one(start, finish);
char *const fld_begin = start;
read_token(start, finish);
char *const fld_end = start;
char *wp = fld_begin;
if (is_null_expression(fld_begin, fld_end)) {
/* null */
flds[i] = string_ref();
} else {
unescape_string(wp, fld_begin, fld_end); /* in-place */
flds[i] = string_ref(fld_begin, wp);
}
}
cur_row_offset = start - readbuf.begin();
return &flds[0];
}
void
hstcpcli::response_buf_remove()
{
if (response_end_offset == 0) {
close();
set_error(-1, "response_buf_remove: protocol out of sync");
return;
}
readbuf.erase_front(response_end_offset);
response_end_offset = 0;
--num_req_rcvd;
cur_row_offset = 0;
num_flds = 0;
flds.clear();
}
hstcpcli_ptr
hstcpcli_i::create(const socket_args& args)
{
return hstcpcli_ptr(new hstcpcli(args));
}
};
// vim:sw=2:ai
/*
* Copyright (C) 2010 DeNA Co.,Ltd.. All rights reserved.
* See COPYRIGHT.txt for details.
*/
#ifndef DENA_HSTCPCLI_HPP
#define DENA_HSTCPCLI_HPP
#include <sys/types.h>
#include <sys/socket.h>
#include <string>
#include <memory>
#include "config.hpp"
#include "socket.hpp"
#include "string_ref.hpp"
#include "string_buffer.hpp"
namespace dena {
struct hstcpcli_filter {
string_ref filter_type;
string_ref op;
size_t ff_offset;
string_ref val;
hstcpcli_filter() : ff_offset(0) { }
};
struct hstcpcli_i;
typedef std::auto_ptr<hstcpcli_i> hstcpcli_ptr;
struct hstcpcli_i {
virtual ~hstcpcli_i() { }
virtual void close() = 0;
virtual int reconnect() = 0;
virtual bool stable_point() = 0;
virtual void request_buf_open_index(size_t pst_id, const char *dbn,
const char *tbl, const char *idx, const char *retflds,
const char *filflds = 0) = 0;
virtual void request_buf_exec_generic(size_t pst_id, const string_ref& op,
const string_ref *kvs, size_t kvslen, uint32_t limit, uint32_t skip,
const string_ref& mod_op, const string_ref *mvs, size_t mvslen,
const hstcpcli_filter *fils = 0, size_t filslen = 0) = 0;
virtual int request_send() = 0;
virtual int response_recv(size_t& num_flds_r) = 0;
virtual const string_ref *get_next_row() = 0;
virtual void response_buf_remove() = 0;
virtual int get_error_code() = 0;
virtual std::string get_error() = 0;
static hstcpcli_ptr create(const socket_args& args);
};
};
#endif
Summary: handlersocket client library
Name: libhsclient
Version: HANDLERSOCKET_VERSION
Release: 1%{?dist}
Group: System Environment/Libraries
License: BSD
Source: libhsclient.tar.gz
Packager: Akira Higuchi <higuchi dot akira at dena dot jp>
BuildRoot: /var/tmp/%{name}-%{version}-root
%description
%prep
%setup -n %{name}
%define _use_internal_dependency_generator 0
%build
make -f Makefile.plain
%install
rm -rf $RPM_BUILD_ROOT
mkdir -p $RPM_BUILD_ROOT/usr/include/handlersocket
mkdir -p $RPM_BUILD_ROOT/%{_bindir}
mkdir -p $RPM_BUILD_ROOT/%{_libdir}
install -m 755 libhsclient.a $RPM_BUILD_ROOT/%{_libdir}
install -m 644 *.hpp $RPM_BUILD_ROOT/usr/include/handlersocket/
%post
/sbin/ldconfig
%postun
/sbin/ldconfig
%files
%defattr(-, root, root)
/usr/include/*
%{_libdir}/*.a
// vim:sw=2:ai
/*
* Copyright (C) 2010 DeNA Co.,Ltd.. All rights reserved.
* See COPYRIGHT.txt for details.
*/
#ifndef DENA_MUTEX_HPP
#define DENA_MUTEX_HPP
#include <pthread.h>
#include <stdlib.h>
#include "fatal.hpp"
#include "util.hpp"
namespace dena {
struct condition;
struct mutex : private noncopyable {
friend struct condition;
mutex() {
if (pthread_mutex_init(&mtx, 0) != 0) {
fatal_abort("pthread_mutex_init");
}
}
~mutex() {
if (pthread_mutex_destroy(&mtx) != 0) {
fatal_abort("pthread_mutex_destroy");
}
}
void lock() const {
if (pthread_mutex_lock(&mtx) != 0) {
fatal_abort("pthread_mutex_lock");
}
}
void unlock() const {
if (pthread_mutex_unlock(&mtx) != 0) {
fatal_abort("pthread_mutex_unlock");
}
}
private:
mutable pthread_mutex_t mtx;
};
};
#endif
// vim:sw=2:ai
/*
* Copyright (C) 2010 DeNA Co.,Ltd.. All rights reserved.
* See COPYRIGHT.txt for details.
*/
#include <stdexcept>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/un.h>
#include "socket.hpp"
#include "string_util.hpp"
#include "fatal.hpp"
namespace dena {
void
ignore_sigpipe()
{
if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) {
fatal_abort("SIGPIPE SIG_IGN");
}
}
void
socket_args::set(const config& conf)
{
timeout = conf.get_int("timeout", 600);
listen_backlog = conf.get_int("listen_backlog", 256);
std::string node = conf.get_str("host", "");
std::string port = conf.get_str("port", "");
if (!node.empty() || !port.empty()) {
if (family == AF_UNIX || node == "/") {
set_unix_domain(port.c_str());
} else {
const char *nd = node.empty() ? 0 : node.c_str();
if (resolve(nd, port.c_str()) != 0) {
fatal_exit("getaddrinfo failed: " + node + ":" + port);
}
}
}
sndbuf = conf.get_int("sndbuf", 0);
rcvbuf = conf.get_int("rcvbuf", 0);
}
void
socket_args::set_unix_domain(const char *path)
{
family = AF_UNIX;
addr = sockaddr_storage();
addrlen = sizeof(sockaddr_un);
sockaddr_un *const ap = reinterpret_cast<sockaddr_un *>(&addr);
ap->sun_family = AF_UNIX;
strncpy(ap->sun_path, path, sizeof(ap->sun_path) - 1);
}
int
socket_args::resolve(const char *node, const char *service)
{
const int flags = (node == 0) ? AI_PASSIVE : 0;
auto_addrinfo ai;
addr = sockaddr_storage();
addrlen = 0;
const int r = ai.resolve(node, service, flags, family, socktype, protocol);
if (r != 0) {
return r;
}
memcpy(&addr, ai.get()->ai_addr, ai.get()->ai_addrlen);
addrlen = ai.get()->ai_addrlen;
return 0;
}
int
socket_set_options(auto_file& fd, const socket_args& args, std::string& err_r)
{
if (args.timeout != 0 && !args.nonblocking) {
struct timeval tv;
tv.tv_sec = args.timeout;
tv.tv_usec = 0;
if (setsockopt(fd.get(), SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) != 0) {
return errno_string("setsockopt SO_RCVTIMEO", errno, err_r);
}
tv.tv_sec = args.timeout;
tv.tv_usec = 0;
if (setsockopt(fd.get(), SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)) != 0) {
return errno_string("setsockopt SO_RCVTIMEO", errno, err_r);
}
}
if (args.nonblocking && fcntl(fd.get(), F_SETFL, O_NONBLOCK) != 0) {
return errno_string("fcntl O_NONBLOCK", errno, err_r);
}
if (args.sndbuf != 0) {
const int v = args.sndbuf;
if (setsockopt(fd.get(), SOL_SOCKET, SO_SNDBUF, &v, sizeof(v)) != 0) {
return errno_string("setsockopt SO_SNDBUF", errno, err_r);
}
}
if (args.rcvbuf != 0) {
const int v = args.rcvbuf;
if (setsockopt(fd.get(), SOL_SOCKET, SO_RCVBUF, &v, sizeof(v)) != 0) {
return errno_string("setsockopt SO_RCVBUF", errno, err_r);
}
}
return 0;
}
int
socket_open(auto_file& fd, const socket_args& args, std::string& err_r)
{
fd.reset(socket(args.family, args.socktype, args.protocol));
if (fd.get() < 0) {
return errno_string("socket", errno, err_r);
}
return socket_set_options(fd, args, err_r);
}
int
socket_connect(auto_file& fd, const socket_args& args, std::string& err_r)
{
int r = 0;
if ((r = socket_open(fd, args, err_r)) != 0) {
return r;
}
if (connect(fd.get(), reinterpret_cast<const sockaddr *>(&args.addr),
args.addrlen) != 0) {
if (!args.nonblocking || errno != EINPROGRESS) {
return errno_string("connect", errno, err_r);
}
}
return 0;
}
int
socket_bind(auto_file& fd, const socket_args& args, std::string& err_r)
{
fd.reset(socket(args.family, args.socktype, args.protocol));
if (fd.get() < 0) {
return errno_string("socket", errno, err_r);
}
if (args.reuseaddr) {
if (args.family == AF_UNIX) {
const sockaddr_un *const ap =
reinterpret_cast<const sockaddr_un *>(&args.addr);
if (unlink(ap->sun_path) != 0 && errno != ENOENT) {
return errno_string("unlink uds", errno, err_r);
}
} else {
int v = 1;
if (setsockopt(fd.get(), SOL_SOCKET, SO_REUSEADDR, &v, sizeof(v)) != 0) {
return errno_string("setsockopt SO_REUSEADDR", errno, err_r);
}
}
}
if (bind(fd.get(), reinterpret_cast<const sockaddr *>(&args.addr),
args.addrlen) != 0) {
return errno_string("bind", errno, err_r);
}
if (listen(fd.get(), args.listen_backlog) != 0) {
return errno_string("listen", errno, err_r);
}
if (args.nonblocking && fcntl(fd.get(), F_SETFL, O_NONBLOCK) != 0) {
return errno_string("fcntl O_NONBLOCK", errno, err_r);
}
return 0;
}
int
socket_accept(int listen_fd, auto_file& fd, const socket_args& args,
sockaddr_storage& addr_r, socklen_t& addrlen_r, std::string& err_r)
{
fd.reset(accept(listen_fd, reinterpret_cast<sockaddr *>(&addr_r),
&addrlen_r));
if (fd.get() < 0) {
return errno_string("accept", errno, err_r);
}
return socket_set_options(fd, args, err_r);
}
};
// vim:sw=2:ai
/*
* Copyright (C) 2010 DeNA Co.,Ltd.. All rights reserved.
* See COPYRIGHT.txt for details.
*/
#ifndef DENA_SOCKET_HPP
#define DENA_SOCKET_HPP
#include <string>
#include "auto_addrinfo.hpp"
#include "auto_file.hpp"
#include "config.hpp"
namespace dena {
struct socket_args {
sockaddr_storage addr;
socklen_t addrlen;
int family;
int socktype;
int protocol;
int timeout;
int listen_backlog;
bool reuseaddr;
bool nonblocking;
bool use_epoll;
int sndbuf;
int rcvbuf;
socket_args() : addr(), addrlen(0), family(AF_INET), socktype(SOCK_STREAM),
protocol(0), timeout(600), listen_backlog(256),
reuseaddr(true), nonblocking(false), use_epoll(false),
sndbuf(0), rcvbuf(0) { }
void set(const config& conf);
void set_unix_domain(const char *path);
int resolve(const char *node, const char *service);
};
void ignore_sigpipe();
int socket_bind(auto_file& fd, const socket_args& args, std::string& err_r);
int socket_connect(auto_file& fd, const socket_args& args, std::string& err_r);
int socket_accept(int listen_fd, auto_file& fd, const socket_args& args,
sockaddr_storage& addr_r, socklen_t& addrlen_r, std::string& err_r);
};
#endif
// vim:sw=2:ai
/*
* Copyright (C) 2010 DeNA Co.,Ltd.. All rights reserved.
* See COPYRIGHT.txt for details.
*/
#ifndef DENA_STRING_BUFFER_HPP
#define DENA_STRING_BUFFER_HPP
#include <vector>
#include <stdlib.h>
#include <string.h>
#include "util.hpp"
#include "allocator.hpp"
#include "fatal.hpp"
namespace dena {
struct string_buffer : private noncopyable {
string_buffer() : buffer(0), begin_offset(0), end_offset(0), alloc_size(0) { }
~string_buffer() {
DENA_FREE(buffer);
}
const char *begin() const {
return buffer + begin_offset;
}
const char *end() const {
return buffer + end_offset;
}
char *begin() {
return buffer + begin_offset;
}
char *end() {
return buffer + end_offset;
}
size_t size() const {
return end_offset - begin_offset;
}
void clear() {
begin_offset = end_offset = 0;
}
void resize(size_t len) {
if (size() < len) {
reserve(len);
memset(buffer + end_offset, 0, len - size());
}
end_offset = begin_offset + len;
}
void reserve(size_t len) {
if (alloc_size >= begin_offset + len) {
return;
}
size_t asz = alloc_size;
while (asz < begin_offset + len) {
if (asz == 0) {
asz = 16;
}
const size_t asz_n = asz << 1;
if (asz_n < asz) {
fatal_abort("string_buffer::resize() overflow");
}
asz = asz_n;
}
void *const p = DENA_REALLOC(buffer, asz);
if (p == 0) {
fatal_abort("string_buffer::resize() realloc");
}
buffer = static_cast<char *>(p);
alloc_size = asz;
}
void erase_front(size_t len) {
if (len >= size()) {
clear();
} else {
begin_offset += len;
}
}
char *make_space(size_t len) {
reserve(size() + len);
return buffer + end_offset;
}
void space_wrote(size_t len) {
len = std::min(len, alloc_size - end_offset);
end_offset = std::min(end_offset + len, alloc_size);
}
template <size_t N>
void append_literal(const char (& str)[N]) {
append(str, str + N - 1);
}
void append(const char *start, const char *finish) {
const size_t len = finish - start;
reserve(size() + len);
memcpy(buffer + end_offset, start, len);
end_offset += len;
}
void append_2(const char *s1, const char *f1, const char *s2,
const char *f2) {
const size_t l1 = f1 - s1;
const size_t l2 = f2 - s2;
reserve(end_offset + l1 + l2);
memcpy(buffer + end_offset, s1, l1);
memcpy(buffer + end_offset + l1, s2, l2);
end_offset += l1 + l2;
}
private:
char *buffer;
size_t begin_offset;
size_t end_offset;
size_t alloc_size;
};
};
#endif
// vim:sw=2:ai
/*
* Copyright (C) 2010 DeNA Co.,Ltd.. All rights reserved.
* See COPYRIGHT.txt for details.
*/
#ifndef DENA_STRING_REF_HPP
#define DENA_STRING_REF_HPP
#include <vector>
#include <string.h>
namespace dena {
struct string_wref {
typedef char value_type;
char *begin() const { return start; }
char *end() const { return start + length; }
size_t size() const { return length; }
private:
char *start;
size_t length;
public:
string_wref(char *s = 0, size_t len = 0) : start(s), length(len) { }
};
struct string_ref {
typedef const char value_type;
const char *begin() const { return start; }
const char *end() const { return start + length; }
size_t size() const { return length; }
private:
const char *start;
size_t length;
public:
string_ref(const char *s = 0, size_t len = 0) : start(s), length(len) { }
string_ref(const char *s, const char *f) : start(s), length(f - s) { }
string_ref(const string_wref& w) : start(w.begin()), length(w.size()) { }
};
template <size_t N> inline bool
operator ==(const string_ref& x, const char (& y)[N]) {
return (x.size() == N - 1) && (::memcmp(x.begin(), y, N - 1) == 0);
}
inline bool
operator ==(const string_ref& x, const string_ref& y) {
return (x.size() == y.size()) &&
(::memcmp(x.begin(), y.begin(), x.size()) == 0);
}
inline bool
operator !=(const string_ref& x, const string_ref& y) {
return (x.size() != y.size()) ||
(::memcmp(x.begin(), y.begin(), x.size()) != 0);
}
};
#endif
// vim:sw=2:ai
/*
* Copyright (C) 2010 DeNA Co.,Ltd.. All rights reserved.
* See COPYRIGHT.txt for details.
*/
#include <errno.h>
#include <stdio.h>
#include "string_util.hpp"
namespace dena {
string_wref
get_token(char *& wp, char *wp_end, char delim)
{
char *const wp_begin = wp;
char *const p = memchr_char(wp_begin, delim, wp_end - wp_begin);
if (p == 0) {
wp = wp_end;
return string_wref(wp_begin, wp_end - wp_begin);
}
wp = p + 1;
return string_wref(wp_begin, p - wp_begin);
}
template <typename T> T
atoi_tmpl_nocheck(const char *start, const char *finish)
{
T v = 0;
for (; start != finish; ++start) {
const char c = *start;
if (c < '0' || c > '9') {
break;
}
v *= 10;
v += static_cast<T>(c - '0');
}
return v;
}
template <typename T> T
atoi_signed_tmpl_nocheck(const char *start, const char *finish)
{
T v = 0;
bool negative = false;
if (start != finish) {
if (start[0] == '-') {
++start;
negative = true;
} else if (start[0] == '+') {
++start;
}
}
for (; start != finish; ++start) {
const char c = *start;
if (c < '0' || c > '9') {
break;
}
v *= 10;
if (negative) {
v -= static_cast<T>(c - '0');
} else {
v += static_cast<T>(c - '0');
}
}
return v;
}
uint32_t
atoi_uint32_nocheck(const char *start, const char *finish)
{
return atoi_tmpl_nocheck<uint32_t>(start, finish);
}
long long
atoll_nocheck(const char *start, const char *finish)
{
return atoi_signed_tmpl_nocheck<long long>(start, finish);
}
void
append_uint32(string_buffer& buf, uint32_t v)
{
char *const wp = buf.make_space(64);
const int len = snprintf(wp, 64, "%lu", static_cast<unsigned long>(v));
if (len > 0) {
buf.space_wrote(len);
}
}
std::string
to_stdstring(uint32_t v)
{
char buf[64];
snprintf(buf, sizeof(buf), "%lu", static_cast<unsigned long>(v));
return std::string(buf);
}
int
errno_string(const char *s, int en, std::string& err_r)
{
char buf[64];
snprintf(buf, sizeof(buf), "%s: %d", s, en);
err_r = std::string(buf);
return en;
}
template <typename T> size_t
split_tmpl_arr(char delim, const T& buf, T *parts, size_t parts_len)
{
typedef typename T::value_type value_type;
size_t i = 0;
value_type *start = buf.begin();
value_type *const finish = buf.end();
for (i = 0; i < parts_len; ++i) {
value_type *const p = memchr_char(start, delim, finish - start);
if (p == 0) {
parts[i] = T(start, finish - start);
++i;
break;
}
parts[i] = T(start, p - start);
start = p + 1;
}
const size_t r = i;
for (; i < parts_len; ++i) {
parts[i] = T();
}
return r;
}
size_t
split(char delim, const string_ref& buf, string_ref *parts,
size_t parts_len)
{
return split_tmpl_arr(delim, buf, parts, parts_len);
}
size_t
split(char delim, const string_wref& buf, string_wref *parts,
size_t parts_len)
{
return split_tmpl_arr(delim, buf, parts, parts_len);
}
template <typename T, typename V> size_t
split_tmpl_vec(char delim, const T& buf, V& parts)
{
typedef typename T::value_type value_type;
size_t i = 0;
value_type *start = buf.begin();
value_type *const finish = buf.end();
while (true) {
value_type *const p = memchr_char(start, delim, finish - start);
if (p == 0) {
parts.push_back(T(start, finish - start));
break;
}
parts.push_back(T(start, p - start));
start = p + 1;
}
const size_t r = i;
return r;
}
size_t
split(char delim, const string_ref& buf, std::vector<string_ref>& parts_r)
{
return split_tmpl_vec(delim, buf, parts_r);
}
size_t
split(char delim, const string_wref& buf, std::vector<string_wref>& parts_r)
{
return split_tmpl_vec(delim, buf, parts_r);
}
};
// vim:sw=2:ai
/*
* Copyright (C) 2010 DeNA Co.,Ltd.. All rights reserved.
* See COPYRIGHT.txt for details.
*/
#ifndef DENA_STRING_UTIL_HPP
#define DENA_STRING_UTIL_HPP
#include <string>
#include <string.h>
#include <stdint.h>
#include "string_buffer.hpp"
#include "string_ref.hpp"
namespace dena {
inline const char *
memchr_char(const char *s, int c, size_t n)
{
return static_cast<const char *>(memchr(s, c, n));
}
inline char *
memchr_char(char *s, int c, size_t n)
{
return static_cast<char *>(memchr(s, c, n));
}
string_wref get_token(char *& wp, char *wp_end, char delim);
uint32_t atoi_uint32_nocheck(const char *start, const char *finish);
std::string to_stdstring(uint32_t v);
void append_uint32(string_buffer& buf, uint32_t v);
long long atoll_nocheck(const char *start, const char *finish);
int errno_string(const char *s, int en, std::string& err_r);
size_t split(char delim, const string_ref& buf, string_ref *parts,
size_t parts_len);
size_t split(char delim, const string_wref& buf, string_wref *parts,
size_t parts_len);
size_t split(char delim, const string_ref& buf,
std::vector<string_ref>& parts_r);
size_t split(char delim, const string_wref& buf,
std::vector<string_wref>& parts_r);
};
#endif
// vim:sw=2:ai
/*
* Copyright (C) 2010 DeNA Co.,Ltd.. All rights reserved.
* See COPYRIGHT.txt for details.
*/
#ifndef DENA_THREAD_HPP
#define DENA_THREAD_HPP
#include <stdexcept>
#include <pthread.h>
#include "fatal.hpp"
namespace dena {
template <typename T>
struct thread : private noncopyable {
template <typename Ta> thread(const Ta& arg, size_t stack_sz = 256 * 1024)
: obj(arg), thr(0), need_join(false), stack_size(stack_sz) { }
template <typename Ta0, typename Ta1> thread(const Ta0& a0,
volatile Ta1& a1, size_t stack_sz = 256 * 1024)
: obj(a0, a1), thr(0), need_join(false), stack_size(stack_sz) { }
~thread() {
join();
}
void start() {
if (!start_nothrow()) {
fatal_abort("thread::start");
}
}
bool start_nothrow() {
if (need_join) {
return need_join; /* true */
}
void *const arg = this;
pthread_attr_t attr;
if (pthread_attr_init(&attr) != 0) {
fatal_abort("pthread_attr_init");
}
if (pthread_attr_setstacksize(&attr, stack_size) != 0) {
fatal_abort("pthread_attr_setstacksize");
}
const int r = pthread_create(&thr, &attr, thread_main, arg);
if (pthread_attr_destroy(&attr) != 0) {
fatal_abort("pthread_attr_destroy");
}
if (r != 0) {
return need_join; /* false */
}
need_join = true;
return need_join; /* true */
}
void join() {
if (!need_join) {
return;
}
int e = 0;
if ((e = pthread_join(thr, 0)) != 0) {
fatal_abort("pthread_join");
}
need_join = false;
}
T& operator *() { return obj; }
T *operator ->() { return &obj; }
private:
static void *thread_main(void *arg) {
thread *p = static_cast<thread *>(arg);
p->obj();
return 0;
}
private:
T obj;
pthread_t thr;
bool need_join;
size_t stack_size;
};
};
#endif
// vim:sw=2:ai
/*
* Copyright (C) 2010 DeNA Co.,Ltd.. All rights reserved.
* See COPYRIGHT.txt for details.
*/
#ifndef DENA_UTIL_HPP
#define DENA_UTIL_HPP
namespace dena {
/* boost::noncopyable */
struct noncopyable {
noncopyable() { }
private:
noncopyable(const noncopyable&);
noncopyable& operator =(const noncopyable&);
};
};
#endif
[a@c54hdd libhsclient]$ ./hstest_hs.sh host=192.168.100.104 key_mask=1000000 num_threads=100 num=10000000 timelimit=10 dbname=hstest
now: 1274127653 cntdiff: 265538 tdiff: 1.000996 rps: 265273.757409
now: 1274127654 cntdiff: 265762 tdiff: 1.000995 rps: 265497.850684
now: 1274127655 cntdiff: 265435 tdiff: 1.001010 rps: 265167.196749
now: 1274127656 cntdiff: 265144 tdiff: 1.000994 rps: 264880.654203
now: 1274127657 cntdiff: 265593 tdiff: 1.000995 rps: 265329.018659
now: 1274127658 cntdiff: 264863 tdiff: 1.000996 rps: 264599.492138
now: 1274127659 cntdiff: 265688 tdiff: 1.001008 rps: 265420.447231
now: 1274127660 cntdiff: 265727 tdiff: 1.000999 rps: 265461.810594
now: 1274127661 cntdiff: 265848 tdiff: 1.001010 rps: 265579.716809
now: 1274127662 cntdiff: 265430 tdiff: 1.000992 rps: 265167.001723
now: 1274127663 cntdiff: 266379 tdiff: 1.001008 rps: 266110.751381
now: 1274127664 cntdiff: 266244 tdiff: 1.001003 rps: 265977.217679
now: 1274127665 cntdiff: 265737 tdiff: 1.000996 rps: 265472.559379
now: 1274127666 cntdiff: 265878 tdiff: 1.001003 rps: 265611.647683
(1274127656.104648: 1328292, 1274127666.114649: 3985679), 265473.20173 qps
*************************** 1. row ***************************
Type: InnoDB
Name:
Status:
=====================================
100518 5:18:13 INNODB MONITOR OUTPUT
=====================================
Per second averages calculated from the last 5 seconds
----------
BACKGROUND THREAD
----------
srv_master_thread loops: 191 1_second, 190 sleeps, 18 10_second, 5 background, 5 flush
srv_master_thread log flush and writes: 190
----------
SEMAPHORES
----------
OS WAIT ARRAY INFO: reservation count 53519, signal count 29547
Mutex spin waits 3083488, rounds 5159906, OS waits 50700
RW-shared spins 21, OS waits 16; RW-excl spins 1, OS waits 4
Spin rounds per wait: 1.67 mutex, 30.00 RW-shared, 151.00 RW-excl
------------
TRANSACTIONS
------------
Trx id counter EDA36085
Purge done for trx's n:o < EC1F94A7 undo n:o < 0
History list length 20
LIST OF TRANSACTIONS FOR EACH SESSION:
---TRANSACTION 0, not started, process no 4533, OS thread id 1079281984
MySQL thread id 11, query id 16 localhost root
show engine innodb status
---TRANSACTION ED9D5959, not started, process no 4533, OS thread id 1089849664
MySQL thread id 7, query id 0 handlersocket: mode=rd, 0 conns, 0 active
---TRANSACTION ED9D5956, not started, process no 4533, OS thread id 1238796608
MySQL thread id 1, query id 0 handlersocket: mode=rd, 0 conns, 0 active
---TRANSACTION EDA36084, not started, process no 4533, OS thread id 1255582016
mysql tables in use 1, locked 1
MySQL thread id 3, query id 0 handlersocket: mode=rd, 12 conns, 7 active
---TRANSACTION EDA36080, not started, process no 4533, OS thread id 1247189312
mysql tables in use 1, locked 1
MySQL thread id 2, query id 0 handlersocket: mode=rd, 36 conns, 18 active
---TRANSACTION EDA36082, ACTIVE 0 sec, process no 4533, OS thread id 1263974720 committing
MySQL thread id 4, query id 0 handlersocket: mode=rd, 37 conns, 20 active
Trx read view will not see trx with id >= EDA36083, sees < EDA3607D
---TRANSACTION EDA3607D, ACTIVE 0 sec, process no 4533, OS thread id 1272367424, thread declared inside InnoDB 500
mysql tables in use 1, locked 1
MySQL thread id 5, query id 0 handlersocket: mode=rd, 15 conns, 9 active
Trx read view will not see trx with id >= EDA3607E, sees < EDA36079
--------
FILE I/O
--------
I/O thread 0 state: waiting for i/o request (insert buffer thread)
I/O thread 1 state: waiting for i/o request (log thread)
I/O thread 2 state: waiting for i/o request (read thread)
I/O thread 3 state: waiting for i/o request (read thread)
I/O thread 4 state: waiting for i/o request (read thread)
I/O thread 5 state: waiting for i/o request (read thread)
I/O thread 6 state: waiting for i/o request (write thread)
I/O thread 7 state: waiting for i/o request (write thread)
I/O thread 8 state: waiting for i/o request (write thread)
I/O thread 9 state: waiting for i/o request (write thread)
Pending normal aio reads: 0, aio writes: 0,
ibuf aio reads: 0, log i/o's: 0, sync i/o's: 0
Pending flushes (fsync) log: 0; buffer pool: 0
71 OS file reads, 235 OS file writes, 235 OS fsyncs
0.00 reads/s, 0 avg bytes/read, 1.00 writes/s, 1.00 fsyncs/s
-------------------------------------
INSERT BUFFER AND ADAPTIVE HASH INDEX
-------------------------------------
Ibuf: size 1, free list len 0, seg size 2,
0 inserts, 0 merged recs, 0 merges
Hash table size 12750011, node heap has 2 buffer(s)
267203.76 hash searches/s, 0.00 non-hash searches/s
---
LOG
---
Log sequence number 147179727377
Log flushed up to 147179726685
Last checkpoint at 147179716475
0 pending log writes, 0 pending chkp writes
194 log i/o's done, 1.00 log i/o's/second
----------------------
BUFFER POOL AND MEMORY
----------------------
Total memory allocated 6587154432; in additional pool allocated 0
Dictionary memory allocated 33640
Buffer pool size 393216
Free buffers 393154
Database pages 60
Old database pages 0
Modified db pages 1
Pending reads 0
Pending writes: LRU 0, flush list 0, single page 0
Pages made young 0, not young 0
0.00 youngs/s, 0.00 non-youngs/s
Pages read 60, created 0, written 23
0.00 reads/s, 0.00 creates/s, 0.00 writes/s
Buffer pool hit rate 1000 / 1000, young-making rate 0 / 1000 not 0 / 1000
Pages read ahead 0.00/s, evicted without access 0.00/s
LRU len: 60, unzip_LRU len: 0
I/O sum[0]:cur[0], unzip sum[0]:cur[0]
--------------
ROW OPERATIONS
--------------
2 queries inside InnoDB, 0 queries in queue
3 read views open inside InnoDB
Main thread process no. 4533, id 1230403904, state: sleeping
Number of rows inserted 0, updated 0, deleted 0, read 37653556
0.00 inserts/s, 0.00 updates/s, 0.00 deletes/s, 266608.28 reads/s
----------------------------
END OF INNODB MONITOR OUTPUT
============================
[a@c54hdd libhsclient]$ ./hstest_my.sh host=192.168.100.104 key_mask=1000000 num_threads=100 num=10000000 timelimit=10 dbname=hstest
now: 1274128046 cntdiff: 63061 tdiff: 1.000999 rps: 62998.066579
now: 1274128047 cntdiff: 61227 tdiff: 1.001013 rps: 61165.037337
now: 1274128048 cntdiff: 61367 tdiff: 1.001029 rps: 61303.917375
now: 1274128049 cntdiff: 61959 tdiff: 1.000962 rps: 61899.451554
now: 1274128050 cntdiff: 62176 tdiff: 1.001006 rps: 62113.520756
now: 1274128051 cntdiff: 61367 tdiff: 1.000998 rps: 61305.815559
now: 1274128052 cntdiff: 61644 tdiff: 1.001015 rps: 61581.497988
now: 1274128053 cntdiff: 60659 tdiff: 1.000984 rps: 60599.373036
now: 1274128054 cntdiff: 59459 tdiff: 1.000996 rps: 59399.831067
now: 1274128055 cntdiff: 62310 tdiff: 1.001011 rps: 62247.074757
now: 1274128056 cntdiff: 61947 tdiff: 1.000991 rps: 61885.664744
now: 1274128057 cntdiff: 60675 tdiff: 1.001006 rps: 60614.029076
now: 1274128058 cntdiff: 60312 tdiff: 1.001001 rps: 60251.680861
now: 1274128059 cntdiff: 60290 tdiff: 1.001004 rps: 60229.530717
(1274128049.309634: 309654, 1274128059.319648: 920493), 61022.79143 qps
*************************** 1. row ***************************
Type: InnoDB
Name:
Status:
=====================================
100518 5:24:51 INNODB MONITOR OUTPUT
=====================================
Per second averages calculated from the last 5 seconds
----------
BACKGROUND THREAD
----------
srv_master_thread loops: 220 1_second, 219 sleeps, 21 10_second, 6 background, 6 flush
srv_master_thread log flush and writes: 219
----------
SEMAPHORES
----------
OS WAIT ARRAY INFO: reservation count 56193, signal count 30826
Mutex spin waits 3415153, rounds 5618661, OS waits 53251
RW-shared spins 24, OS waits 17; RW-excl spins 1, OS waits 5
Spin rounds per wait: 1.65 mutex, 30.00 RW-shared, 181.00 RW-excl
------------
TRANSACTIONS
------------
Trx id counter EDB514D6
Purge done for trx's n:o < EC1F94A7 undo n:o < 0
History list length 20
LIST OF TRANSACTIONS FOR EACH SESSION:
---TRANSACTION 0, not started, process no 4533, OS thread id 1306585408
MySQL thread id 113, query id 920620 localhost root
show engine innodb status
---TRANSACTION EDA708BB, not started, process no 4533, OS thread id 1272367424
MySQL thread id 5, query id 0 handlersocket: mode=rd, 0 conns, 0 active
---TRANSACTION ED9D5959, not started, process no 4533, OS thread id 1089849664
MySQL thread id 7, query id 0 handlersocket: mode=rd, 0 conns, 0 active
---TRANSACTION ED9D5956, not started, process no 4533, OS thread id 1238796608
MySQL thread id 1, query id 0 handlersocket: mode=rd, 0 conns, 0 active
---TRANSACTION EDA708BD, not started, process no 4533, OS thread id 1255582016
MySQL thread id 3, query id 0 handlersocket: mode=rd, 0 conns, 0 active
---TRANSACTION EDA708BF, not started, process no 4533, OS thread id 1247189312
MySQL thread id 2, query id 0 handlersocket: mode=rd, 0 conns, 0 active
---TRANSACTION EDA708BE, not started, process no 4533, OS thread id 1263974720
MySQL thread id 4, query id 0 handlersocket: mode=rd, 0 conns, 0 active
--------
FILE I/O
--------
I/O thread 0 state: waiting for i/o request (insert buffer thread)
I/O thread 1 state: waiting for i/o request (log thread)
I/O thread 2 state: waiting for i/o request (read thread)
I/O thread 3 state: waiting for i/o request (read thread)
I/O thread 4 state: waiting for i/o request (read thread)
I/O thread 5 state: waiting for i/o request (read thread)
I/O thread 6 state: waiting for i/o request (write thread)
I/O thread 7 state: waiting for i/o request (write thread)
I/O thread 8 state: waiting for i/o request (write thread)
I/O thread 9 state: waiting for i/o request (write thread)
Pending normal aio reads: 0, aio writes: 0,
ibuf aio reads: 0, log i/o's: 0, sync i/o's: 0
Pending flushes (fsync) log: 0; buffer pool: 0
71 OS file reads, 269 OS file writes, 269 OS fsyncs
0.00 reads/s, 0 avg bytes/read, 2.40 writes/s, 2.40 fsyncs/s
-------------------------------------
INSERT BUFFER AND ADAPTIVE HASH INDEX
-------------------------------------
Ibuf: size 1, free list len 0, seg size 2,
0 inserts, 0 merged recs, 0 merges
Hash table size 12750011, node heap has 2 buffer(s)
65739.45 hash searches/s, 0.00 non-hash searches/s
---
LOG
---
Log sequence number 147179774153
Log flushed up to 147179771813
Last checkpoint at 147179761899
0 pending log writes, 0 pending chkp writes
220 log i/o's done, 1.60 log i/o's/second
----------------------
BUFFER POOL AND MEMORY
----------------------
Total memory allocated 6587154432; in additional pool allocated 0
Dictionary memory allocated 33640
Buffer pool size 393216
Free buffers 393154
Database pages 60
Old database pages 0
Modified db pages 1
Pending reads 0
Pending writes: LRU 0, flush list 0, single page 0
Pages made young 0, not young 0
0.00 youngs/s, 0.00 non-youngs/s
Pages read 60, created 0, written 27
0.00 reads/s, 0.00 creates/s, 0.40 writes/s
Buffer pool hit rate 1000 / 1000, young-making rate 0 / 1000 not 0 / 1000
Pages read ahead 0.00/s, evicted without access 0.00/s
LRU len: 60, unzip_LRU len: 0
I/O sum[0]:cur[0], unzip sum[0]:cur[0]
--------------
ROW OPERATIONS
--------------
0 queries inside InnoDB, 0 queries in queue
1 read views open inside InnoDB
Main thread process no. 4533, id 1230403904, state: sleeping
Number of rows inserted 0, updated 0, deleted 0, read 40071920
0.00 inserts/s, 0.00 updates/s, 0.00 deletes/s, 66321.54 reads/s
----------------------------
END OF INNODB MONITOR OUTPUT
============================
Copyright (c) 2010 DeNA Co.,Ltd.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of DeNA Co.,Ltd. nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY DeNA Co.,Ltd. "AS IS" AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
EVENT SHALL DeNA Co.,Ltd. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Revision history for Perl extension HandlerSocket.
0.01 Wed Mar 31 11:50:23 2010
- original version; created by h2xs 1.23 with options
-A -n HandlerSocket
// vim:ai:sw=2:ts=8
/*
* Copyright (C) 2010 DeNA Co.,Ltd.. All rights reserved.
* See COPYRIGHT.txt for details.
*/
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
#include "ppport.h"
#include "hstcpcli.hpp"
#define DBG(x)
static SV *
arr_get_entry(AV *av, I32 avmax, I32 idx)
{
if (idx > avmax) {
DBG(fprintf(stderr, "arr_get_entry1 %d %d\n", avmax, idx));
return 0;
}
SV **const ev = av_fetch(av, idx, 0);
if (ev == 0) {
DBG(fprintf(stderr, "arr_get_entry2 %d %d\n", avmax, idx));
return 0;
}
return *ev;
}
static int
arr_get_intval(AV *av, I32 avmax, I32 idx, int default_val = 0)
{
SV *const e = arr_get_entry(av, avmax, idx);
if (e == 0) {
return default_val;
}
return SvIV(e);
}
static const char *
sv_get_strval(SV *sv)
{
if (sv == 0 || !SvPOK(sv)) {
DBG(fprintf(stderr, "sv_get_strval\n"));
return 0;
}
return SvPV_nolen(sv);
}
static const char *
arr_get_strval(AV *av, I32 avmax, I32 idx)
{
SV *const e = arr_get_entry(av, avmax, idx);
return sv_get_strval(e);
}
static AV *
sv_get_arrval(SV *sv)
{
if (sv == 0 || !SvROK(sv)) {
DBG(fprintf(stderr, "sv_get_arrval1\n"));
return 0;
}
SV *const svtarget = SvRV(sv);
if (svtarget == 0 || SvTYPE(svtarget) != SVt_PVAV) {
DBG(fprintf(stderr, "sv_get_arrval2\n"));
return 0;
}
return (AV *)svtarget;
}
static AV *
arr_get_arrval(AV *av, I32 avmax, I32 idx)
{
SV *const e = arr_get_entry(av, avmax, idx);
if (e == 0) {
DBG(fprintf(stderr, "arr_get_arrval1\n"));
return 0;
}
return sv_get_arrval(e);
}
static void
hv_to_strmap(HV *hv, std::map<std::string, std::string>& m_r)
{
if (hv == 0) {
return;
}
hv_iterinit(hv);
HE *hent = 0;
while ((hent = hv_iternext(hv)) != 0) {
I32 klen = 0;
char *const k = hv_iterkey(hent, &klen);
DBG(fprintf(stderr, "k=%s\n", k));
const std::string key(k, klen);
SV *const vsv = hv_iterval(hv, hent);
STRLEN vlen = 0;
char *const v = SvPV(vsv, vlen);
DBG(fprintf(stderr, "v=%s\n", v));
const std::string val(v, vlen);
m_r[key] = val;
}
}
static void
strrefarr_push_back(std::vector<dena::string_ref>& a_r, SV *sv)
{
if (sv == 0 || SvTYPE(sv) == SVt_NULL) {
DBG(fprintf(stderr, "strrefarr_push_back: null\n"));
return a_r.push_back(dena::string_ref());
}
STRLEN vlen = 0;
char *const v = SvPV(sv, vlen);
DBG(fprintf(stderr, "strrefarr_push_back: %s\n", v));
a_r.push_back(dena::string_ref(v, vlen));
}
static void
av_to_strrefarr(AV *av, std::vector<dena::string_ref>& a_r)
{
if (av == 0) {
return;
}
const I32 len = av_len(av) + 1;
for (I32 i = 0; i < len; ++i) {
SV **const ev = av_fetch(av, i, 0);
strrefarr_push_back(a_r, ev ? *ev : 0);
}
}
static dena::string_ref
sv_get_string_ref(SV *sv)
{
if (sv == 0) {
return dena::string_ref();
}
STRLEN vlen = 0;
char *const v = SvPV(sv, vlen);
return dena::string_ref(v, vlen);
}
static IV
sv_get_iv(SV *sv)
{
if (sv == 0 || !SvIOK(sv)) {
return 0;
}
return SvIV(sv);
}
static void
av_to_filters(AV *av, std::vector<dena::hstcpcli_filter>& f_r)
{
DBG(fprintf(stderr, "av_to_filters: %p\n", av));
if (av == 0) {
return;
}
const I32 len = av_len(av) + 1;
DBG(fprintf(stderr, "av_to_filters: len=%d\n", (int)len));
for (I32 i = 0; i < len; ++i) {
AV *const earr = arr_get_arrval(av, len, i);
if (earr == 0) {
continue;
}
const I32 earrlen = av_len(earr) + 1;
dena::hstcpcli_filter fe;
fe.filter_type = sv_get_string_ref(arr_get_entry(earr, earrlen, 0));
fe.op = sv_get_string_ref(arr_get_entry(earr, earrlen, 1));
fe.ff_offset = sv_get_iv(arr_get_entry(earr, earrlen, 2));
fe.val = sv_get_string_ref(arr_get_entry(earr, earrlen, 3));
f_r.push_back(fe);
DBG(fprintf(stderr, "av_to_filters: %s %s %d %s\n",
fe.filter_action.begin(), fe.filter_op.begin(), (int)fe.ff_offset,
fe.value.begin()));
}
}
static void
set_process_verbose_level(const std::map<std::string, std::string>& m)
{
std::map<std::string, std::string>::const_iterator iter = m.find("verbose");
if (iter != m.end()) {
dena::verbose_level = atoi(iter->second.c_str());
}
}
static AV *
execute_internal(SV *obj, int id, const char *op, AV *keys, int limit,
int skip, const char *modop, AV *modvals, AV *filters)
{
AV *retval = (AV *)&PL_sv_undef;
dena::hstcpcli_i *const ptr =
reinterpret_cast<dena::hstcpcli_i *>(SvIV(SvRV(obj)));
do {
std::vector<dena::string_ref> keyarr, mvarr;
std::vector<dena::hstcpcli_filter> farr;
av_to_strrefarr(keys, keyarr);
dena::string_ref modop_ref;
if (modop != 0) {
modop_ref = dena::string_ref(modop, strlen(modop));
av_to_strrefarr(modvals, mvarr);
}
if (filters != 0) {
av_to_filters(filters, farr);
}
ptr->request_buf_exec_generic(id, dena::string_ref(op, strlen(op)),
&keyarr[0], keyarr.size(), limit, skip, modop_ref, &mvarr[0],
mvarr.size(), &farr[0], farr.size());
AV *const av = newAV();
retval = av;
if (ptr->request_send() != 0) {
break;
}
size_t nflds = 0;
ptr->response_recv(nflds);
const int e = ptr->get_error_code();
DBG(fprintf(stderr, "e=%d nflds=%zu\n", e, nflds));
av_push(av, newSViv(e));
if (e != 0) {
const std::string s = ptr->get_error();
av_push(av, newSVpvn(s.data(), s.size()));
} else {
const dena::string_ref *row = 0;
while ((row = ptr->get_next_row()) != 0) {
DBG(fprintf(stderr, "row=%p\n", row));
for (size_t i = 0; i < nflds; ++i) {
const dena::string_ref& v = row[i];
DBG(fprintf(stderr, "FLD %zu v=%s vbegin=%p\n", i,
std::string(v.begin(), v.size())
.c_str(), v.begin()));
if (v.begin() != 0) {
SV *const e = newSVpvn(
v.begin(), v.size());
av_push(av, e);
} else {
av_push(av, &PL_sv_undef);
}
}
}
}
if (e >= 0) {
ptr->response_buf_remove();
}
} while (0);
return retval;
}
struct execute_arg {
int id;
const char *op;
AV *keys;
int limit;
int skip;
const char *modop;
AV *modvals;
AV *filters;
execute_arg() : id(0), op(0), keys(0), limit(0), skip(0), modop(0),
modvals(0), filters(0) { }
};
static AV *
execute_multi_internal(SV *obj, const execute_arg *args, size_t num_args)
{
dena::hstcpcli_i *const ptr =
reinterpret_cast<dena::hstcpcli_i *>(SvIV(SvRV(obj)));
/* appends multiple requests to the send buffer */
for (size_t i = 0; i < num_args; ++i) {
std::vector<dena::string_ref> keyarr, mvarr;
std::vector<dena::hstcpcli_filter> farr;
const execute_arg& arg = args[i];
av_to_strrefarr(arg.keys, keyarr);
dena::string_ref modop_ref;
if (arg.modop != 0) {
modop_ref = dena::string_ref(arg.modop, strlen(arg.modop));
av_to_strrefarr(arg.modvals, mvarr);
}
if (arg.filters != 0) {
av_to_filters(arg.filters, farr);
}
ptr->request_buf_exec_generic(arg.id,
dena::string_ref(arg.op, strlen(arg.op)), &keyarr[0], keyarr.size(),
arg.limit, arg.skip, modop_ref, &mvarr[0], mvarr.size(), &farr[0],
farr.size());
}
AV *const retval = newAV();
/* sends the requests */
if (ptr->request_send() < 0) {
/* IO error */
AV *const av_respent = newAV();
av_push(retval, newRV_noinc((SV *)av_respent));
av_push(av_respent, newSViv(ptr->get_error_code()));
const std::string& s = ptr->get_error();
av_push(av_respent, newSVpvn(s.data(), s.size()));
return retval; /* retval : [ [ err_code, err_message ] ] */
}
/* receives responses */
for (size_t i = 0; i < num_args; ++i) {
AV *const av_respent = newAV();
av_push(retval, newRV_noinc((SV *)av_respent));
size_t nflds = 0;
const int e = ptr->response_recv(nflds);
av_push(av_respent, newSViv(e));
if (e != 0) {
const std::string& s = ptr->get_error();
av_push(av_respent, newSVpvn(s.data(), s.size()));
} else {
const dena::string_ref *row = 0;
while ((row = ptr->get_next_row()) != 0) {
for (size_t i = 0; i < nflds; ++i) {
const dena::string_ref& v = row[i];
DBG(fprintf(stderr, "%zu %s\n", i,
std::string(v.begin(), v.size()).c_str()));
if (v.begin() != 0) {
av_push(av_respent, newSVpvn(v.begin(), v.size()));
} else {
/* null */
av_push(av_respent, &PL_sv_undef);
}
}
}
}
if (e >= 0) {
ptr->response_buf_remove();
}
if (e < 0) {
return retval;
}
}
return retval;
}
MODULE = Net::HandlerSocket PACKAGE = Net::HandlerSocket
SV *
new(klass, args)
char *klass
HV *args
CODE:
RETVAL = &PL_sv_undef;
dena::config conf;
hv_to_strmap(args, conf);
set_process_verbose_level(conf);
dena::socket_args sargs;
sargs.set(conf);
dena::hstcpcli_ptr p = dena::hstcpcli_i::create(sargs);
SV *const objref = newSViv(0);
SV *const obj = newSVrv(objref, klass);
dena::hstcpcli_i *const ptr = p.get();
sv_setiv(obj, reinterpret_cast<IV>(ptr));
p.release();
SvREADONLY_on(obj);
RETVAL = objref;
OUTPUT:
RETVAL
void
DESTROY(obj)
SV *obj
CODE:
dena::hstcpcli_i *const ptr =
reinterpret_cast<dena::hstcpcli_i *>(SvIV(SvRV(obj)));
delete ptr;
void
close(obj)
SV *obj
CODE:
dena::hstcpcli_i *const ptr =
reinterpret_cast<dena::hstcpcli_i *>(SvIV(SvRV(obj)));
ptr->close();
int
reconnect(obj)
SV *obj
CODE:
RETVAL = 0;
dena::hstcpcli_i *const ptr =
reinterpret_cast<dena::hstcpcli_i *>(SvIV(SvRV(obj)));
RETVAL = ptr->reconnect();
OUTPUT:
RETVAL
int
stable_point(obj)
SV *obj
CODE:
RETVAL = 0;
dena::hstcpcli_i *const ptr =
reinterpret_cast<dena::hstcpcli_i *>(SvIV(SvRV(obj)));
const bool rv = ptr->stable_point();
RETVAL = static_cast<int>(rv);
OUTPUT:
RETVAL
int
get_error_code(obj)
SV *obj
CODE:
RETVAL = 0;
dena::hstcpcli_i *const ptr =
reinterpret_cast<dena::hstcpcli_i *>(SvIV(SvRV(obj)));
RETVAL = ptr->get_error_code();
OUTPUT:
RETVAL
SV *
get_error(obj)
SV *obj
CODE:
RETVAL = &PL_sv_undef;
dena::hstcpcli_i *const ptr =
reinterpret_cast<dena::hstcpcli_i *>(SvIV(SvRV(obj)));
const std::string s = ptr->get_error();
RETVAL = newSVpvn(s.data(), s.size());
OUTPUT:
RETVAL
int
open_index(obj, id, db, table, index, fields, ffields = 0)
SV *obj
int id
const char *db
const char *table
const char *index
const char *fields
SV *ffields
CODE:
const char *const ffields_str = sv_get_strval(ffields);
RETVAL = 0;
dena::hstcpcli_i *const ptr =
reinterpret_cast<dena::hstcpcli_i *>(SvIV(SvRV(obj)));
do {
ptr->request_buf_open_index(id, db, table, index, fields, ffields_str);
if (ptr->request_send() != 0) {
break;
}
size_t nflds = 0;
ptr->response_recv(nflds);
const int e = ptr->get_error_code();
DBG(fprintf(stderr, "errcode=%d\n", ptr->get_error_code()));
if (e >= 0) {
ptr->response_buf_remove();
}
DBG(fprintf(stderr, "errcode=%d\n", ptr->get_error_code()));
} while (0);
RETVAL = ptr->get_error_code();
OUTPUT:
RETVAL
AV *
execute_single(obj, id, op, keys, limit, skip, mop = 0, mvs = 0, fils = 0)
SV *obj
int id
const char *op
AV *keys
int limit
int skip
SV *mop
SV *mvs
SV *fils
CODE:
const char *const mop_str = sv_get_strval(mop);
AV *const mvs_av = sv_get_arrval(mvs);
AV *const fils_av = sv_get_arrval(fils);
RETVAL = execute_internal(obj, id, op, keys, limit, skip, mop_str, mvs_av,
fils_av);
sv_2mortal((SV *)RETVAL);
OUTPUT:
RETVAL
AV *
execute_multi(obj, cmds)
SV *obj
AV *cmds
CODE:
DBG(fprintf(stderr, "execute_multi0\n"));
const I32 cmdsmax = av_len(cmds);
execute_arg args[cmdsmax + 1]; /* GNU */
for (I32 i = 0; i <= cmdsmax; ++i) {
AV *const avtarget = arr_get_arrval(cmds, cmdsmax, i);
if (avtarget == 0) {
DBG(fprintf(stderr, "execute_multi1 %d\n", i));
continue;
}
const I32 argmax = av_len(avtarget);
if (argmax < 2) {
DBG(fprintf(stderr, "execute_multi2 %d\n", i));
continue;
}
execute_arg& ag = args[i];
ag.id = arr_get_intval(avtarget, argmax, 0);
ag.op = arr_get_strval(avtarget, argmax, 1);
ag.keys = arr_get_arrval(avtarget, argmax, 2);
ag.limit = arr_get_intval(avtarget, argmax, 3);
ag.skip = arr_get_intval(avtarget, argmax, 4);
ag.modop = arr_get_strval(avtarget, argmax, 5);
ag.modvals = arr_get_arrval(avtarget, argmax, 6);
ag.filters = arr_get_arrval(avtarget, argmax, 7);
DBG(fprintf(stderr, "execute_multi3 %d: %d %s %p %d %d %s %p %p\n",
i, ag.id, ag.op, ag.keys, ag.limit, ag.skip, ag.modop, ag.modvals,
ag.filters));
}
RETVAL = execute_multi_internal(obj, args, cmdsmax + 1);
sv_2mortal((SV *)RETVAL);
OUTPUT:
RETVAL
AV *
execute_find(obj, id, op, keys, limit, skip, mop = 0, mvs = 0, fils = 0)
SV *obj
int id
const char *op
AV *keys
int limit
int skip
SV *mop
SV *mvs
SV *fils
CODE:
const char *const mop_str = sv_get_strval(mop);
AV *const mvs_av = sv_get_arrval(mvs);
AV *const fils_av = sv_get_arrval(fils);
RETVAL = execute_internal(obj, id, op, keys, limit, skip, mop_str, mvs_av,
fils_av);
sv_2mortal((SV *)RETVAL);
OUTPUT:
RETVAL
AV *
execute_update(obj, id, op, keys, limit, skip, modvals, fils = 0)
SV *obj
int id
const char *op
AV *keys
int limit
int skip
AV *modvals
SV *fils
CODE:
AV *const fils_av = sv_get_arrval(fils);
RETVAL = execute_internal(obj, id, op, keys, limit, skip, "U",
modvals, fils_av);
sv_2mortal((SV *)RETVAL);
OUTPUT:
RETVAL
AV *
execute_delete(obj, id, op, keys, limit, skip, fils = 0)
SV *obj
int id
const char *op
AV *keys
int limit
int skip
SV *fils
CODE:
AV *const fils_av = sv_get_arrval(fils);
RETVAL = execute_internal(obj, id, op, keys, limit, skip, "D", 0, fils_av);
sv_2mortal((SV *)RETVAL);
OUTPUT:
RETVAL
AV *
execute_insert(obj, id, fvals)
SV *obj
int id
AV *fvals
CODE:
RETVAL = execute_internal(obj, id, "+", fvals, 0, 0, 0, 0, 0);
sv_2mortal((SV *)RETVAL);
OUTPUT:
RETVAL
Changes
HandlerSocket.xs
Makefile.PL
MANIFEST
ppport.h
README
t/HandlerSocket.t
lib/HandlerSocket.pm
# use 5.010000;
use ExtUtils::MakeMaker;
# See lib/ExtUtils/MakeMaker.pm for details of how to influence
# the contents of the Makefile that is written.
WriteMakefile(
NAME => 'Net::HandlerSocket',
VERSION_FROM => 'lib/Net/HandlerSocket.pm', # finds $VERSION
PREREQ_PM => {}, # e.g., Module::Name => 1.1
($] >= 5.005 ? ## Add these new keywords supported since 5.005
(ABSTRACT_FROM => 'lib/Net/HandlerSocket.pm', # retrieve abstract from module
AUTHOR => 'higuchi dot akira at dena dot jp>') : ()),
CC => 'g++ -fPIC',
LD => 'g++ -fPIC',
LIBS => ['-L../libhsclient -L../libhsclient/.libs -lhsclient'],
DEFINE => '',
INC => '-I. -I../libhsclient',
OPTIMIZE => '-g -O3 -Wall -Wno-unused',
);
# use 5.010000;
use ExtUtils::MakeMaker;
# See lib/ExtUtils/MakeMaker.pm for details of how to influence
# the contents of the Makefile that is written.
WriteMakefile(
NAME => 'Net::HandlerSocket',
VERSION_FROM => 'lib/Net/HandlerSocket.pm', # finds $VERSION
PREREQ_PM => {}, # e.g., Module::Name => 1.1
($] >= 5.005 ? ## Add these new keywords supported since 5.005
(ABSTRACT_FROM => 'lib/Net/HandlerSocket.pm', # retrieve abstract from module
AUTHOR => 'higuchi dot akira at dena dot jp>') : ()),
CC => 'g++ -fPIC',
LD => 'g++ -fPIC',
LIBS => ['-lhsclient'], # e.g., '-lm'
DEFINE => '', # e.g., '-DHAVE_SOMETHING'
INC => '-I. -I/usr/include/handlersocket',
OPTIMIZE => '-g -O3 -Wall -Wno-unused',
# Un-comment this if you add C files to link with later:
# OBJECT => '$(O_FILES)', # link all the C files too
);
HandlerSocket version 0.01
==========================
The README is used to introduce the module and provide instructions on
how to install the module, any machine dependencies it may have (for
example C compilers and installed libraries) and any other information
that should be provided before the module is installed.
A README file is required for CPAN modules since CPAN extracts the
README file from a module distribution so that people browsing the
archive can use it get an idea of the modules uses. It is usually a
good idea to provide version information here so that people can
decide whether fixes for the module are worth downloading.
INSTALLATION
To install this module type the following:
perl Makefile.PL
make
make test
make install
DEPENDENCIES
COPYRIGHT AND LICENCE
Copyright (C) 2010 DeNA Co.,Ltd.. All rights reserved.
See COPYRIGHT.txt for details.
package Net::HandlerSocket;
use strict;
use warnings;
require Exporter;
our @ISA = qw(Exporter);
# Items to export into callers namespace by default. Note: do not export
# names by default without a very good reason. Use EXPORT_OK instead.
# Do not simply export all your public functions/methods/constants.
# This allows declaration use Net::HandlerSocket ':all';
# If you do not need this, moving things directly into @EXPORT or @EXPORT_OK
# will save memory.
our %EXPORT_TAGS = ( 'all' => [ qw(
) ] );
our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
our @EXPORT = qw(
);
our $VERSION = '0.01';
require XSLoader;
XSLoader::load('Net::HandlerSocket', $VERSION);
# Preloaded methods go here.
1;
__END__
# Below is stub documentation for your module. You'd better edit it!
=head1 NAME
Net::HandlerSocket - Perl extension for blah blah blah
=head1 SYNOPSIS
use Net::HandlerSocket;
my $hsargs = { host => 'localhost', port => 9999 };
my $cli = new Net::HandlerSocket($hsargs);
$cli->open_index(1, 'testdb', 'testtable1', 'PRIMARY', 'foo,bar,baz');
$cli->open_index(2, 'testdb', 'testtable2', 'i2', 'hoge,fuga');
$cli->execute_find(1, '>=', [ 'aaa', 'bbb' ], 5, 100);
# select foo,bar,baz from testdb.testtable1
# where pk1 = 'aaa' and pk2 = 'bbb' order by pk1, pk2
# limit 100, 5
=head1 DESCRIPTION
Stub documentation for Net::HandlerSocket, created by h2xs.
=head1 AUTHOR
Akira HiguchiE<lt>higuchi dot akira at dena dot jpE<gt>
=head1 COPYRIGHT AND LICENSE
Copyright (C) 2010 DeNA Co.,Ltd.. All rights reserved.
See COPYRIGHT.txt for details.
=cut
#!/usr/bin/perl
package Net::HandlerSocket::HSPool;
use strict;
use warnings;
use Net::HandlerSocket;
use Socket;
sub new {
my $self = {
config => $_[1],
reopen_interval => 60,
hostmap => { },
};
return bless $self, $_[0];
}
sub clear_pool {
my ($self) = @_;
$self->{hostmap} = { };
}
sub on_error {
my ($self, $obj) = @_;
my $error_func = $self->{config}->{error};
if (defined($error_func)) {
return &{$error_func}($obj);
}
die $obj;
}
sub on_warning {
my ($self, $obj) = @_;
my $warning_func = $self->{config}->{warning};
if (defined($warning_func)) {
return &{$warning_func}($obj);
}
}
sub get_conf {
my ($self, $dbtbl) = @_;
my $hcent = $self->{config}->{hostmap}->{$dbtbl};
if (!defined($hcent)) {
$self->on_error("get_conf: $dbtbl not found");
return undef;
}
my %cpy = %$hcent;
$cpy{port} ||= 9998;
$cpy{timeout} ||= 2;
return \%cpy;
}
sub resolve_hostname {
my ($self, $hcent, $host_ip_list) = @_;
if (defined($host_ip_list)) {
if (scalar(@$host_ip_list) > 0) {
$hcent->{host} = shift(@$host_ip_list);
return $host_ip_list;
}
return undef; # no more ip
}
my $host = $hcent->{host}; # unresolved name
$hcent->{hostname} = $host;
my $resolve_list_func = $self->{config}->{resolve_list};
if (defined($resolve_list_func)) {
$host_ip_list = &{$resolve_list_func}($host);
if (scalar(@$host_ip_list) > 0) {
$hcent->{host} = shift(@$host_ip_list);
return $host_ip_list;
}
return undef; # no more ip
}
my $resolve_func = $self->{config}->{resolve};
if (defined($resolve_func)) {
$hcent->{host} = &{$resolve_func}($host);
return [];
}
my $packed = gethostbyname($host);
if (!defined($packed)) {
return undef;
}
$hcent->{host} = inet_ntoa($packed);
return [];
}
sub get_handle_exec {
my ($self, $db, $tbl, $idx, $cols, $exec_multi, $exec_args) = @_;
my $now = time();
my $dbtbl = join('.', $db, $tbl);
my $hcent = $self->get_conf($dbtbl); # copy
if (!defined($hcent)) {
return undef;
}
my $hmkey = join(':', $hcent->{host}, $hcent->{port});
my $hment = $self->{hostmap}->{$hmkey};
# [ open_time, handle, index_map, host, next_index_id ]
my $host_ip_list;
TRY_OTHER_IP:
if (!defined($hment) ||
$hment->[0] + $self->{reopen_interval} < $now ||
!$hment->[1]->stable_point()) {
$host_ip_list = $self->resolve_hostname($hcent, $host_ip_list);
if (!defined($host_ip_list)) {
my $hostport = $hmkey . '(' . $hcent->{host} . ')';
$self->on_error("HSPool::get_handle" .
"($db, $tbl, $idx, $cols): host=$hmkey: " .
"no more active ip");
return undef;
}
my $hnd = new Net::HandlerSocket($hcent);
my %m = ();
$hment = [ $now, $hnd, \%m, $hcent->{host}, 1 ];
$self->{hostmap}->{$hmkey} = $hment;
}
my $hnd = $hment->[1];
my $idxmap = $hment->[2];
my $imkey = join(':', $idx, $cols);
my $idx_id = $idxmap->{$imkey};
if (!defined($idx_id)) {
$idx_id = $hment->[4];
my $e = $hnd->open_index($idx_id, $db, $tbl, $idx, $cols);
if ($e != 0) {
my $estr = $hnd->get_error();
my $hostport = $hmkey . '(' . $hcent->{host} . ')';
my $errmess = "HSPool::get_handle open_index" .
"($db, $tbl, $idx, $cols): host=$hostport " .
"err=$e($estr)";
$self->on_warning($errmess);
$hnd->close();
$hment = undef;
goto TRY_OTHER_IP;
}
$hment->[4]++;
$idxmap->{$imkey} = $idx_id;
}
if ($exec_multi) {
my $resarr;
for my $cmdent (@$exec_args) {
$cmdent->[0] = $idx_id;
}
if (scalar(@$exec_args) == 0) {
$resarr = [];
} else {
$resarr = $hnd->execute_multi($exec_args);
}
my $i = 0;
for my $res (@$resarr) {
if ($res->[0] != 0) {
my $cmdent = $exec_args->[$i];
my $ec = $res->[0];
my $estr = $res->[1];
my $op = $cmdent->[1];
my $kfvs = $cmdent->[2];
my $kvstr = defined($kfvs)
? join(',', @$kfvs) : '';
my $limit = $cmdent->[3] || 0;
my $skip = $cmdent->[4] || 0;
my $hostport = $hmkey . '(' . $hcent->{host}
. ')';
my $errmess = "HSPool::get_handle execm" .
"($db, $tbl, $idx, [$cols], " .
"($idx_id), $op, [$kvstr] " .
"$limit, $skip): " .
"host=$hostport err=$ec($estr)";
if ($res->[0] < 0 || $res->[0] == 2) {
$self->on_warning($errmess);
$hnd->close();
$hment = undef;
goto TRY_OTHER_IP;
} else {
$self->on_error($errmess);
}
}
shift(@$res);
++$i;
}
return $resarr;
} else {
my $res = $hnd->execute_find($idx_id, @$exec_args);
if ($res->[0] != 0) {
my ($op, $kfvals, $limit, $skip) = @$exec_args;
my $ec = $res->[0];
my $estr = $res->[1];
my $kvstr = join(',', @$kfvals);
my $hostport = $hmkey . '(' . $hcent->{host} . ')';
my $errmess = "HSPool::get_handle exec" .
"($db, $tbl, $idx, [$cols], ($idx_id), " .
"$op, [$kvstr], $limit, $skip): " .
"host=$hostport err=$ec($estr)";
if ($res->[0] < 0 || $res->[0] == 2) {
$self->on_warning($errmess);
$hnd->close();
$hment = undef;
goto TRY_OTHER_IP;
} else {
$self->on_error($errmess);
}
}
shift(@$res);
return $res;
}
}
sub index_find {
my ($self, $db, $tbl, $idx, $cols, $op, $kfvals, $limit, $skip) = @_;
# cols: comma separated list
# kfvals: arrayref
$limit ||= 0;
$skip ||= 0;
my $res = $self->get_handle_exec($db, $tbl, $idx, $cols,
0, [ $op, $kfvals, $limit, $skip ]);
return $res;
}
sub index_find_multi {
my ($self, $db, $tbl, $idx, $cols, $cmdlist) = @_;
# cols : comma separated list
# cmdlist : [ dummy, op, kfvals, limit, skip ]
# kfvals : arrayref
my $resarr = $self->get_handle_exec($db, $tbl, $idx, $cols,
1, $cmdlist);
return $resarr;
}
sub result_single_to_arrarr {
my ($numcols, $hsres, $ret) = @_;
my $hsreslen = scalar(@$hsres);
my $rlen = int($hsreslen / $numcols);
$ret = [ ] if !defined($ret);
my @r = ();
my $p = 0;
for (my $i = 0; $i < $rlen; ++$i) {
my @a = splice(@$hsres, $p, $numcols);
$p += $numcols;
push(@$ret, \@a);
}
return $ret; # arrayref of arrayrefs
}
sub result_multi_to_arrarr {
my ($numcols, $mhsres, $ret) = @_;
$ret = [ ] if !defined($ret);
for my $hsres (@$mhsres) {
my $hsreslen = scalar(@$hsres);
my $rlen = int($hsreslen / $numcols);
my $p = 0;
for (my $i = 0; $i < $rlen; ++$i) {
my @a = splice(@$hsres, $p, $numcols);
$p += $numcols;
push(@$ret, \@a);
}
}
return $ret; # arrayref of arrayrefs
}
sub result_single_to_hasharr {
my ($names, $hsres, $ret) = @_;
my $nameslen = scalar(@$names);
my $hsreslen = scalar(@$hsres);
my $rlen = int($hsreslen / $nameslen);
$ret = [ ] if !defined($ret);
my $p = 0;
for (my $i = 0; $i < $rlen; ++$i) {
my %h = ();
for (my $j = 0; $j < $nameslen; ++$j, ++$p) {
$h{$names->[$j]} = $hsres->[$p];
}
push(@$ret, \%h);
}
return $ret; # arrayref of hashrefs
}
sub result_multi_to_hasharr {
my ($names, $mhsres, $ret) = @_;
my $nameslen = scalar(@$names);
$ret = [ ] if !defined($ret);
for my $hsres (@$mhsres) {
my $hsreslen = scalar(@$hsres);
my $rlen = int($hsreslen / $nameslen);
my $p = 0;
for (my $i = 0; $i < $rlen; ++$i) {
my %h = ();
for (my $j = 0; $j < $nameslen; ++$j, ++$p) {
$h{$names->[$j]} = $hsres->[$p];
}
push(@$ret, \%h);
}
}
return $ret; # arrayref of hashrefs
}
sub result_single_to_hashhash {
my ($names, $key, $hsres, $ret) = @_;
my $nameslen = scalar(@$names);
my $hsreslen = scalar(@$hsres);
my $rlen = int($hsreslen / $nameslen);
$ret = { } if !defined($ret);
my $p = 0;
for (my $i = 0; $i < $rlen; ++$i) {
my %h = ();
for (my $j = 0; $j < $nameslen; ++$j, ++$p) {
$h{$names->[$j]} = $hsres->[$p];
}
my $k = $h{$key};
$ret->{$k} = \%h if defined($k);
}
return $ret; # hashref of hashrefs
}
sub result_multi_to_hashhash {
my ($names, $key, $mhsres, $ret) = @_;
my $nameslen = scalar(@$names);
$ret = { } if !defined($ret);
for my $hsres (@$mhsres) {
my $hsreslen = scalar(@$hsres);
my $rlen = int($hsreslen / $nameslen);
my $p = 0;
for (my $i = 0; $i < $rlen; ++$i) {
my %h = ();
for (my $j = 0; $j < $nameslen; ++$j, ++$p) {
$h{$names->[$j]} = $hsres->[$p];
}
my $k = $h{$key};
$ret->{$k} = \%h if defined($k);
}
}
return $ret; # hashref of hashrefs
}
sub select_cols_where_eq_aa {
# SELECT $cols FROM $db.$tbl WHERE $idx_key = $kv LIMIT 1
my ($self, $db, $tbl, $idx, $cols_aref, $kv_aref) = @_;
my $cols_str = join(',', @$cols_aref);
my $res = $self->index_find($db, $tbl, $idx, $cols_str, '=', $kv_aref);
return result_single_to_arrarr(scalar(@$cols_aref), $res);
}
sub select_cols_where_eq_hh {
# SELECT $cols FROM $db.$tbl WHERE $idx_key = $kv LIMIT 1
my ($self, $db, $tbl, $idx, $cols_aref, $kv_aref, $retkey) = @_;
my $cols_str = join(',', @$cols_aref);
my $res = $self->index_find($db, $tbl, $idx, $cols_str, '=', $kv_aref);
my $r = result_single_to_hashhash($cols_aref, $retkey, $res);
return $r;
}
sub select_cols_where_in_hh {
# SELECT $cols FROM $db.$tbl WHERE $idx_key in ($vals)
my ($self, $db, $tbl, $idx, $cols_aref, $vals_aref, $retkey) = @_;
my $cols_str = join(',', @$cols_aref);
my @cmdlist = ();
for my $v (@$vals_aref) {
push(@cmdlist, [ -1, '=', [ $v ] ]);
}
my $res = $self->index_find_multi($db, $tbl, $idx, $cols_str,
\@cmdlist);
return result_multi_to_hashhash($cols_aref, $retkey, $res);
}
1;
#
# - HandlerSocket -
# This spec file was automatically generated by cpan2rpm [ver: 2.027]
# The following arguments were used:
# --no-sign perl-Net-HandlerSocket.tar.gz
# For more information on cpan2rpm please visit: http://perl.arix.com/
#
%define pkgname perl-Net-HandlerSocket
%define filelist %{pkgname}-%{version}-filelist
%define NVR %{pkgname}-%{version}-%{release}
%define maketest 1
name: perl-Net-HandlerSocket
summary: HandlerSocket - Perl extension for handlersocket
version: HANDLERSOCKET_VERSION
release: 1%{?dist}
packager: Akira Higuchi <higuchi dot akira at dena dot jp>
license: BSD
group: Applications/CPAN
group: System Environment/Libraries
buildroot: %{_tmppath}/%{name}-%{version}-%(id -u -n)
prefix: %(echo %{_prefix})
source: perl-Net-HandlerSocket.tar.gz
BuildRequires: libhsclient
Requires: libhsclient
Obsoletes: perl-DB-HandlerSocket
%description
Stub documentation for HandlerSocket, created by h2xs. It looks like the
author of the extension was negligent enough to leave the stub
unedited.
#
# This package was generated automatically with the cpan2rpm
# utility. To get this software or for more information
# please visit: http://perl.arix.com/
#
%prep
%setup -q -n %{pkgname}
chmod -R u+w %{_builddir}/%{pkgname}
%build
grep -rsl '^#!.*perl' . |
grep -v '.bak$' |xargs --no-run-if-empty \
%__perl -MExtUtils::MakeMaker -e 'MY->fixin(@ARGV)'
CFLAGS="$RPM_OPT_FLAGS"
%{__perl} Makefile.PL.installed `%{__perl} -MExtUtils::MakeMaker -e ' print qq|PREFIX=%{buildroot}%{_prefix}| if \$ExtUtils::MakeMaker::VERSION =~ /5\.9[1-6]|6\.0[0-5]/ '`
%{__make}
%if %maketest
%{__make} test
%endif
%install
[ "%{buildroot}" != "/" ] && rm -rf %{buildroot}
%{makeinstall} `%{__perl} -MExtUtils::MakeMaker -e ' print \$ExtUtils::MakeMaker::VERSION <= 6.05 ? qq|PREFIX=%{buildroot}%{_prefix}| : qq|DESTDIR=%{buildroot}| '`
cmd=/usr/share/spec-helper/compress_files
[ -x $cmd ] || cmd=/usr/lib/rpm/brp-compress
[ -x $cmd ] && $cmd
# SuSE Linux
if [ -e /etc/SuSE-release -o -e /etc/UnitedLinux-release ]
then
%{__mkdir_p} %{buildroot}/var/adm/perl-modules
%{__cat} `find %{buildroot} -name "perllocal.pod"` \
| %{__sed} -e s+%{buildroot}++g \
> %{buildroot}/var/adm/perl-modules/%{name}
fi
# remove special files
find %{buildroot} -name "perllocal.pod" \
-o -name ".packlist" \
-o -name "*.bs" \
|xargs -i rm -f {}
# no empty directories
find %{buildroot}%{_prefix} \
-type d -depth \
-exec rmdir {} \; 2>/dev/null
%{__perl} -MFile::Find -le '
find({ wanted => \&wanted, no_chdir => 1}, "%{buildroot}");
print "%doc Changes README";
for my $x (sort @dirs, @files) {
push @ret, $x unless indirs($x);
}
print join "\n", sort @ret;
sub wanted {
return if /auto$/;
local $_ = $File::Find::name;
my $f = $_; s|^\Q%{buildroot}\E||;
return unless length;
return $files[@files] = $_ if -f $f;
$d = $_;
/\Q$d\E/ && return for reverse sort @INC;
$d =~ /\Q$_\E/ && return
for qw|/etc %_prefix/man %_prefix/bin %_prefix/share|;
$dirs[@dirs] = $_;
}
sub indirs {
my $x = shift;
$x =~ /^\Q$_\E\// && $x ne $_ && return 1 for @dirs;
}
' > %filelist
[ -z %filelist ] && {
echo "ERROR: empty %files listing"
exit -1
}
%clean
[ "%{buildroot}" != "/" ] && rm -rf %{buildroot}
%files -f %filelist
%defattr(-,root,root)
%changelog
* Thu Apr 1 2010 a@localhost.localdomain
- Initial build.
This source diff could not be displayed because it is too large. You can view the blob instead.
# Before `make install' is performed this script should be runnable with
# `make test'. After `make install' it should work as `perl HandlerSocket.t'
#########################
# change 'tests => 1' to 'tests => last_test_to_print';
use Test::More tests => 1;
BEGIN { use_ok('Net::HandlerSocket') };
#########################
# Insert your test code below, the Test::More module is use()ed here so read
# its man page ( perldoc Test::More ) for help writing this test script.
MYSQL_PLUGIN(handlersocket, [HandlerSocket], [HandlerSocket], [max])
MYSQL_PLUGIN_DYNAMIC(handlersocket, handlersocket.la)
MYSQL_PLUGIN_ACTIONS(handlersocket,
[
ac_mysql_source_dir='$(top_srcdir)'
MYSQL_INC="-I$ac_mysql_source_dir/sql"
MYSQL_INC="$MYSQL_INC -I$ac_mysql_source_dir/include"
MYSQL_INC="$MYSQL_INC -I$ac_mysql_source_dir/regex"
MYSQL_INC="$MYSQL_INC -I$ac_mysql_source_dir"
MYSQL_LIB='-L$(top_builddir)/libservices -lmysqlservices'
PLUGIN_DIR='$(pkglibdir)/plugin'
HANDLERSOCKET_SUBDIRS="libhsclient handlersocket client"
AC_SUBST(MYSQL_INC)
AC_SUBST(MYSQL_CFLAGS)
AC_SUBST(MYSQL_LIB)
AC_SUBST(PLUGIN_DIR)
AC_SUBST(HANDLERSOCKET_SUBDIRS)
])
[perl]
default-character-set-name = binary
#!/bin/sh
if [ "`uname -o`" = "Cygwin" ]; then
export DIFF='diff --ignore-space --strip-trailing-cr'
elif [ "`uname`" = "Darwin" ]; then
export DIFF='diff'
else
export DIFF='diff --ignore-space --strip-trailing-cr'
fi
compile_c() {
if [ "`uname -o`" = "Cygwin" ]; then
cl /W3 /I../.. /EHsc /FD /MD "$1" /link /DLL "/OUT:$2.dll" \
../../libase.lib 2> cl.log
else
$CXX -I../.. -O3 -g -Wall -fPIC -shared "$1" -o "$2.so"
fi
}
compile_j() {
if [ "`uname -o`" = "Cygwin" ]; then
jdk="`echo /cygdrive/c/Program\ Files/Java/jdk* | head -1`"
else
jdk="$SUNJDK"
fi
"$jdk/bin/javac" -g "$1"/*.java
"$jdk/bin/jar" -cf "$1.jar" "$1"/*.class
}
# vim:sw=2:ai
package hstest;
use DBI;
use Net::HandlerSocket;
our %conf = ();
sub get_conf_env {
my ($key, $defval) = @_;
return $ENV{$key} || $defval;
}
sub init_conf {
$conf{host} = get_conf_env("MYHOST", "localhost");
$conf{myport} = get_conf_env("MYPORT", 3306);
$conf{dbname} = get_conf_env("MYDBNAME", "hstestdb");
$conf{ssps} = get_conf_env("MYSSPS");
$conf{user} = get_conf_env("MYSQLUSER", "root");
$conf{pass} = get_conf_env("MYSQLPASS", "");
$conf{hsport} = get_conf_env("HSPORT", 9998);
}
sub get_dbi_connection {
my ($dbname, $host, $myport, $ssps, $user, $pass)
= ($conf{dbname}, $conf{host}, $conf{myport}, $conf{ssps},
$conf{user}, $conf{pass});
my $mycnf = "binary_my.cnf";
my $dsn = "DBI:mysql:database=;host=$host;port=$myport"
. ";mysql_server_prepare=$ssps"
. ";mysql_read_default_group=perl"
. ";mysql_read_default_file=../common/$mycnf";
my $dbh = DBI->connect($dsn, $user, $pass, { RaiseError => 1 });
return $dbh;
}
sub init_testdb {
my $charset = $_[0] || "binary";
my $dbh = get_dbi_connection();
my $dbname = $conf{dbname};
$dbh->do("drop database if exists $dbname");
$dbh->do("create database $dbname default character set $charset");
$dbh->do("use $dbname");
return $dbh;
}
sub get_hs_connection {
my ($host, $port) = @_;
$host ||= $conf{host};
$port ||= $conf{hsport};
my $hsargs = { 'host' => $host, 'port' => $port };
my $conn = new Net::HandlerSocket($hsargs);
return $conn;
}
init_conf();
1;
#!/bin/bash
TESTS="01 02 03 04 05 06 07 08 09 10 11 12 13";
source ../common/compat.sh
for i in $TESTS; do
perl "test$i.pl" > test$i.log 2> test$i.log2
done
for i in $TESTS; do
if ! $DIFF -u test$i.log test$i.expected; then
echo "test$i failed";
exit 1
fi
if [ -f "test$i.expect2" ]; then
lines="`wc -l < test$i.expect2`"
head -$lines test$i.log2 > test$i.log2h
if ! $DIFF -u test$i.log2h test$i.expect2 && \
! $DIFF -u test$i.log2h test$i.expect2ef; then
echo "test$i failed";
exit 1
fi
fi
done
echo "OK."
exit 0
k0 v1020
k1 v6351
k10 v70410
k11 v75111
k12 v36712
k13 v40013
k14 v39714
k15 v17015
k16 v71916
k17 v73417
k18 v58718
k19 v49419
k2 v8032
k20 v52320
k21 v95421
k22 v43322
k23 v82023
k24 v28324
k25 v83725
k26 v20526
k27 v41527
k28 v54528
k29 v58329
k3 v9253
k30 v5230
k31 v32331
k32 v61432
k33 v67933
k34 v80534
k35 v45135
k36 v11536
k37 v26937
k38 v21838
k39 v61739
k4 v7754
k40 v87840
k41 v34541
k42 v51242
k43 v96943
k44 v40844
k45 v29145
k46 v85846
k47 v95347
k48 v71048
k49 v14249
k5 v5375
k50 v68250
k51 v93451
k52 v62152
k53 v96553
k54 v57454
k55 v20455
k56 v29856
k57 v13457
k58 v98358
k59 v44459
k6 v5926
k60 v14460
k61 v15261
k62 v18762
k63 v21563
k64 v864
k65 v69765
k66 v65166
k67 v28067
k68 v70168
k69 v53769
k7 v4147
k70 v41370
k71 v6971
k72 v8672
k73 v82273
k74 v67074
k75 v37075
k76 v80676
k77 v68877
k78 v2678
k79 v6679
k8 v5908
k80 v80280
k81 v17181
k82 v55782
k83 v84783
k84 v77784
k85 v73085
k86 v98786
k87 v11587
k88 v64688
k89 v49689
k9 v3029
k90 v12090
k91 v68491
k92 v37492
k93 v6593
k94 v37094
k95 v17495
k96 v82896
k97 v86797
k98 v75998
k99 v70399
#!/usr/bin/perl
# vim:sw=2:ai
# test for libmysql
BEGIN {
push @INC, "../common/";
};
use strict;
use warnings;
use hstest;
my $dbh = hstest::init_testdb();
my $table = 'hstesttbl';
my $tablesize = 100;
$dbh->do(
"create table $table (k varchar(30) primary key, v varchar(30) not null) " .
"engine = innodb");
srand(999);
my %valmap = ();
my $sth = $dbh->prepare("insert into $table values (?,?)");
for (my $i = 0; $i < $tablesize; ++$i) {
my $k = "k" . $i;
my $v = "v" . int(rand(1000)) . $i;
$sth->execute($k, $v);
$valmap{$k} = $v;
}
my $aref = $dbh->selectall_arrayref("select k,v from $table order by k");
for my $row (@$aref) {
my ($k, $v) = @$row;
print "$k $v\n";
}
k0 v1020
k1 v6351
k10 v70410
k11 v75111
k12 v36712
k13 v40013
k14 v39714
k15 v17015
k16 v71916
k17 v73417
k18 v58718
k19 v49419
k2 v8032
k20 v52320
k21 v95421
k22 v43322
k23 v82023
k24 v28324
k25 v83725
k26 v20526
k27 v41527
k28 v54528
k29 v58329
k3 v9253
k30 v5230
k31 v32331
k32 v61432
k33 v67933
k34 v80534
k35 v45135
k36 v11536
k37 v26937
k38 v21838
k39 v61739
k4 v7754
k40 v87840
k41 v34541
k42 v51242
k43 v96943
k44 v40844
k45 v29145
k46 v85846
k47 v95347
k48 v71048
k49 v14249
k5 v5375
k50 v68250
k51 v93451
k52 v62152
k53 v96553
k54 v57454
k55 v20455
k56 v29856
k57 v13457
k58 v98358
k59 v44459
k6 v5926
k60 v14460
k61 v15261
k62 v18762
k63 v21563
k64 v864
k65 v69765
k66 v65166
k67 v28067
k68 v70168
k69 v53769
k7 v4147
k70 v41370
k71 v6971
k72 v8672
k73 v82273
k74 v67074
k75 v37075
k76 v80676
k77 v68877
k78 v2678
k79 v6679
k8 v5908
k80 v80280
k81 v17181
k82 v55782
k83 v84783
k84 v77784
k85 v73085
k86 v98786
k87 v11587
k88 v64688
k89 v49689
k9 v3029
k90 v12090
k91 v68491
k92 v37492
k93 v6593
k94 v37094
k95 v17495
k96 v82896
k97 v86797
k98 v75998
k99 v70399
#!/usr/bin/perl
# vim:sw=2:ai
# test for '>='
BEGIN {
push @INC, "../common/";
};
use strict;
use warnings;
use hstest;
my $dbh = hstest::init_testdb();
my $table = 'hstesttbl';
my $tablesize = 100;
$dbh->do(
"create table $table (k varchar(30) primary key, v varchar(30) not null) " .
"engine = innodb");
srand(999);
my %valmap = ();
my $sth = $dbh->prepare("insert into $table values (?,?)");
for (my $i = 0; $i < $tablesize; ++$i) {
my $k = "k" . $i;
my $v = "v" . int(rand(1000)) . $i;
$sth->execute($k, $v);
$valmap{$k} = $v;
}
my $hs = hstest::get_hs_connection();
my $dbname = $hstest::conf{dbname};
$hs->open_index(1, $dbname, $table, '', 'k,v');
my $r = $hs->execute_single(1, '>=', [ '' ], 10000, 0);
shift(@$r);
for (my $i = 0; $i < $tablesize; ++$i) {
my $k = $r->[$i * 2];
my $v = $r->[$i * 2 + 1];
print "$k $v\n";
}
my $aref = $dbh->selectall_arrayref("select k,v from $table order by k");
for my $row (@$aref) {
my ($k, $v) = @$row;
#print "$k $v\n";
}
WR
0 0
1 1
2 2
3 3
4 4
5 5
6 6
7 7
8 8
9 9
10 10
11 11
12 12
13 13
14 14
15 15
16 16
17 17
18 18
19 19
20 20
21 21
22 22
23 23
24 24
25 25
26 26
27 27
28 28
29 29
30 30
31 31
32 32
33 33
34 34
35 35
36 36
37 37
38 38
39 39
40 40
41 41
42 42
43 43
44 44
45 45
46 46
47 47
48 48
49 49
50 50
51 51
52 52
53 53
54 54
55 55
56 56
57 57
58 58
59 59
60 60
61 61
62 62
63 63
64 64
65 65
66 66
67 67
68 68
69 69
70 70
71 71
72 72
73 73
74 74
75 75
76 76
77 77
78 78
79 79
80 80
81 81
82 82
83 83
84 84
85 85
86 86
87 87
88 88
89 89
90 90
91 91
92 92
93 93
94 94
95 95
96 96
97 97
98 98
99 99
100 100
101 101
102 102
103 103
104 104
105 105
106 106
107 107
108 108
109 109
110 110
111 111
112 112
113 113
114 114
115 115
116 116
117 117
118 118
119 119
120 120
121 121
122 122
123 123
124 124
125 125
126 126
127 127
128 128
129 129
130 130
131 131
132 132
133 133
134 134
135 135
136 136
137 137
138 138
139 139
140 140
141 141
142 142
143 143
144 144
145 145
146 146
147 147
148 148
149 149
150 150
151 151
152 152
153 153
154 154
155 155
156 156
157 157
158 158
159 159
160 160
161 161
162 162
163 163
164 164
165 165
166 166
167 167
168 168
169 169
170 170
171 171
172 172
173 173
174 174
175 175
176 176
177 177
178 178
179 179
180 180
181 181
182 182
183 183
184 184
185 185
186 186
187 187
188 188
189 189
190 190
191 191
192 192
193 193
194 194
195 195
196 196
197 197
198 198
199 199
200 200
201 201
202 202
203 203
204 204
205 205
206 206
207 207
208 208
209 209
210 210
211 211
212 212
213 213
214 214
215 215
216 216
217 217
218 218
219 219
220 220
221 221
222 222
223 223
224 224
225 225
226 226
227 227
228 228
229 229
230 230
231 231
232 232
233 233
234 234
235 235
236 236
237 237
238 238
239 239
240 240
241 241
242 242
243 243
244 244
245 245
246 246
247 247
248 248
249 249
250 250
251 251
252 252
253 253
254 254
255 255
HS
0 0
1 1
10 10
100 100
101 101
102 102
103 103
104 104
105 105
106 106
107 107
108 108
109 109
11 11
110 110
111 111
112 112
113 113
114 114
115 115
116 116
117 117
118 118
119 119
12 12
120 120
121 121
122 122
123 123
124 124
125 125
126 126
127 127
128 128
129 129
13 13
130 130
131 131
132 132
133 133
134 134
135 135
136 136
137 137
138 138
139 139
14 14
140 140
141 141
142 142
143 143
144 144
145 145
146 146
147 147
148 148
149 149
15 15
150 150
151 151
152 152
153 153
154 154
155 155
156 156
157 157
158 158
159 159
16 16
160 160
161 161
162 162
163 163
164 164
165 165
166 166
167 167
168 168
169 169
17 17
170 170
171 171
172 172
173 173
174 174
175 175
176 176
177 177
178 178
179 179
18 18
180 180
181 181
182 182
183 183
184 184
185 185
186 186
187 187
188 188
189 189
19 19
190 190
191 191
192 192
193 193
194 194
195 195
196 196
197 197
198 198
199 199
2 2
20 20
200 200
201 201
202 202
203 203
204 204
205 205
206 206
207 207
208 208
209 209
21 21
210 210
211 211
212 212
213 213
214 214
215 215
216 216
217 217
218 218
219 219
22 22
220 220
221 221
222 222
223 223
224 224
225 225
226 226
227 227
228 228
229 229
23 23
230 230
231 231
232 232
233 233
234 234
235 235
236 236
237 237
238 238
239 239
24 24
240 240
241 241
242 242
243 243
244 244
245 245
246 246
247 247
248 248
249 249
25 25
250 250
251 251
252 252
253 253
254 254
255 255
26 26
27 27
28 28
29 29
3 3
30 30
31 31
32 32
33 33
34 34
35 35
36 36
37 37
38 38
39 39
4 4
40 40
41 41
42 42
43 43
44 44
45 45
46 46
47 47
48 48
49 49
5 5
50 50
51 51
52 52
53 53
54 54
55 55
56 56
57 57
58 58
59 59
6 6
60 60
61 61
62 62
63 63
64 64
65 65
66 66
67 67
68 68
69 69
7 7
70 70
71 71
72 72
73 73
74 74
75 75
76 76
77 77
78 78
79 79
8 8
80 80
81 81
82 82
83 83
84 84
85 85
86 86
87 87
88 88
89 89
9 9
90 90
91 91
92 92
93 93
94 94
95 95
96 96
97 97
98 98
99 99
MY
0 0
1 1
10 10
100 100
101 101
102 102
103 103
104 104
105 105
106 106
107 107
108 108
109 109
11 11
110 110
111 111
112 112
113 113
114 114
115 115
116 116
117 117
118 118
119 119
12 12
120 120
121 121
122 122
123 123
124 124
125 125
126 126
127 127
128 128
129 129
13 13
130 130
131 131
132 132
133 133
134 134
135 135
136 136
137 137
138 138
139 139
14 14
140 140
141 141
142 142
143 143
144 144
145 145
146 146
147 147
148 148
149 149
15 15
150 150
151 151
152 152
153 153
154 154
155 155
156 156
157 157
158 158
159 159
16 16
160 160
161 161
162 162
163 163
164 164
165 165
166 166
167 167
168 168
169 169
17 17
170 170
171 171
172 172
173 173
174 174
175 175
176 176
177 177
178 178
179 179
18 18
180 180
181 181
182 182
183 183
184 184
185 185
186 186
187 187
188 188
189 189
19 19
190 190
191 191
192 192
193 193
194 194
195 195
196 196
197 197
198 198
199 199
2 2
20 20
200 200
201 201
202 202
203 203
204 204
205 205
206 206
207 207
208 208
209 209
21 21
210 210
211 211
212 212
213 213
214 214
215 215
216 216
217 217
218 218
219 219
22 22
220 220
221 221
222 222
223 223
224 224
225 225
226 226
227 227
228 228
229 229
23 23
230 230
231 231
232 232
233 233
234 234
235 235
236 236
237 237
238 238
239 239
24 24
240 240
241 241
242 242
243 243
244 244
245 245
246 246
247 247
248 248
249 249
25 25
250 250
251 251
252 252
253 253
254 254
255 255
26 26
27 27
28 28
29 29
3 3
30 30
31 31
32 32
33 33
34 34
35 35
36 36
37 37
38 38
39 39
4 4
40 40
41 41
42 42
43 43
44 44
45 45
46 46
47 47
48 48
49 49
5 5
50 50
51 51
52 52
53 53
54 54
55 55
56 56
57 57
58 58
59 59
6 6
60 60
61 61
62 62
63 63
64 64
65 65
66 66
67 67
68 68
69 69
7 7
70 70
71 71
72 72
73 73
74 74
75 75
76 76
77 77
78 78
79 79
8 8
80 80
81 81
82 82
83 83
84 84
85 85
86 86
87 87
88 88
89 89
9 9
90 90
91 91
92 92
93 93
94 94
95 95
96 96
97 97
98 98
99 99
#!/usr/bin/perl
# vim:sw=2:ai
# test for binary cleanness (#1)
BEGIN {
push @INC, "../common/";
};
use strict;
use warnings;
use hstest;
my $dbh = hstest::init_testdb();
my $table = 'hstesttbl';
my $tablesize = 256;
$dbh->do(
"create table $table (k varchar(30) primary key, v varchar(30) not null) " .
"engine = innodb default charset = binary");
srand(999);
my %valmap = ();
print "WR\n";
my $sth = $dbh->prepare("insert into $table values (?,?)");
for (my $i = 0; $i < $tablesize; ++$i) {
my $k = "" . $i;
my $v = pack("C", $i);
my $vnum = unpack("C", $v);
print "$k $vnum\n";
$sth->execute($k, $v);
$valmap{$k} = $v;
}
my $hs = hstest::get_hs_connection();
my $dbname = $hstest::conf{dbname};
$hs->open_index(1, $dbname, $table, '', 'k,v');
my $r = $hs->execute_single(1, '>=', [ '' ], 10000, 0);
shift(@$r);
print "HS\n";
for (my $i = 0; $i < $tablesize; ++$i) {
my $k = $r->[$i * 2];
my $v = $r->[$i * 2 + 1];
my $vnum = unpack("C", $v);
print "$k $vnum\n";
print "MISMATCH\n" if ($k ne $vnum);
print "LEN\n" if (length($v) != 1);
}
undef $hs;
print "MY\n";
my $aref = $dbh->selectall_arrayref("select k,v from $table order by k");
for my $row (@$aref) {
my ($k, $v) = @$row;
my $vnum = unpack("C", $v);
print "$k $vnum\n";
print "MISMATCH\n" if ($k ne $vnum);
print "LEN\n" if (length($v) != 1);
}
#!/usr/bin/perl
# vim:sw=2:ai
# test for binary cleanness (#2)
BEGIN {
push @INC, "../common/";
};
use strict;
use warnings;
use hstest;
my $dbh = hstest::init_testdb();
my $table = 'hstesttbl';
my $tablesize = 256;
$dbh->do(
"create table $table (k varchar(30) primary key, v varchar(30) not null) " .
"engine = innodb default charset = binary");
srand(999);
my %valmap = ();
print "WR\n";
my $sth = $dbh->prepare("insert into $table values (?,?)");
for (my $i = 0; $i < $tablesize; ++$i) {
my $k = "" . $i;
my $v = pack("C", $i);
my $vnum = unpack("C", $v);
print "$k $vnum\n";
$sth->execute($k, "a" . $v . "a");
$valmap{$k} = $v;
}
my $hs = hstest::get_hs_connection();
my $dbname = $hstest::conf{dbname};
$hs->open_index(1, $dbname, $table, '', 'k,v');
my $r = $hs->execute_single(1, '>=', [ '' ], 10000, 0);
shift(@$r);
print "HS\n";
for (my $i = 0; $i < $tablesize; ++$i) {
my $k = $r->[$i * 2];
my $v = $r->[$i * 2 + 1];
my $len = length($v);
my $vnum = unpack("C", substr($v, 1, 1));
print "$k $vnum $len [$v]\n";
print "MISMATCH\n" if ($k ne $vnum);
print "LEN\n" if $len != 3;
}
undef $hs;
print "MY\n";
my $aref = $dbh->selectall_arrayref("select k,v from $table order by k");
for my $row (@$aref) {
my ($k, $v) = @$row;
my $len = length($v);
my $vnum = unpack("C", substr($v, 1, 1));
print "$k $vnum $len [$v]\n";
print "MISMATCH\n" if ($k ne $vnum);
print "LEN\n" if $len != 3;
}
WR
0 [null]
1 1
2 [null]
3 3
4 [null]
5 5
6 [null]
7 7
8 [null]
9 9
10 [null]
11 11
12 [null]
13 13
14 [null]
15 15
16 [null]
17 17
18 [null]
19 19
20 [null]
21 21
22 [null]
23 23
24 [null]
25 25
26 [null]
27 27
28 [null]
29 29
30 [null]
31 31
32 [null]
33 33
34 [null]
35 35
36 [null]
37 37
38 [null]
39 39
40 [null]
41 41
42 [null]
43 43
44 [null]
45 45
46 [null]
47 47
48 [null]
49 49
50 [null]
51 51
52 [null]
53 53
54 [null]
55 55
56 [null]
57 57
58 [null]
59 59
60 [null]
61 61
62 [null]
63 63
64 [null]
65 65
66 [null]
67 67
68 [null]
69 69
70 [null]
71 71
72 [null]
73 73
74 [null]
75 75
76 [null]
77 77
78 [null]
79 79
80 [null]
81 81
82 [null]
83 83
84 [null]
85 85
86 [null]
87 87
88 [null]
89 89
90 [null]
91 91
92 [null]
93 93
94 [null]
95 95
96 [null]
97 97
98 [null]
99 99
100 [null]
101 101
102 [null]
103 103
104 [null]
105 105
106 [null]
107 107
108 [null]
109 109
110 [null]
111 111
112 [null]
113 113
114 [null]
115 115
116 [null]
117 117
118 [null]
119 119
120 [null]
121 121
122 [null]
123 123
124 [null]
125 125
126 [null]
127 127
128 [null]
129 129
130 [null]
131 131
132 [null]
133 133
134 [null]
135 135
136 [null]
137 137
138 [null]
139 139
140 [null]
141 141
142 [null]
143 143
144 [null]
145 145
146 [null]
147 147
148 [null]
149 149
150 [null]
151 151
152 [null]
153 153
154 [null]
155 155
156 [null]
157 157
158 [null]
159 159
160 [null]
161 161
162 [null]
163 163
164 [null]
165 165
166 [null]
167 167
168 [null]
169 169
170 [null]
171 171
172 [null]
173 173
174 [null]
175 175
176 [null]
177 177
178 [null]
179 179
180 [null]
181 181
182 [null]
183 183
184 [null]
185 185
186 [null]
187 187
188 [null]
189 189
190 [null]
191 191
192 [null]
193 193
194 [null]
195 195
196 [null]
197 197
198 [null]
199 199
200 [null]
201 201
202 [null]
203 203
204 [null]
205 205
206 [null]
207 207
208 [null]
209 209
210 [null]
211 211
212 [null]
213 213
214 [null]
215 215
216 [null]
217 217
218 [null]
219 219
220 [null]
221 221
222 [null]
223 223
224 [null]
225 225
226 [null]
227 227
228 [null]
229 229
230 [null]
231 231
232 [null]
233 233
234 [null]
235 235
236 [null]
237 237
238 [null]
239 239
240 [null]
241 241
242 [null]
243 243
244 [null]
245 245
246 [null]
247 247
248 [null]
249 249
250 [null]
251 251
252 [null]
253 253
254 [null]
255 255
HS
0 [null]
1 1
10 [null]
100 [null]
101 101
102 [null]
103 103
104 [null]
105 105
106 [null]
107 107
108 [null]
109 109
11 11
110 [null]
111 111
112 [null]
113 113
114 [null]
115 115
116 [null]
117 117
118 [null]
119 119
12 [null]
120 [null]
121 121
122 [null]
123 123
124 [null]
125 125
126 [null]
127 127
128 [null]
129 129
13 13
130 [null]
131 131
132 [null]
133 133
134 [null]
135 135
136 [null]
137 137
138 [null]
139 139
14 [null]
140 [null]
141 141
142 [null]
143 143
144 [null]
145 145
146 [null]
147 147
148 [null]
149 149
15 15
150 [null]
151 151
152 [null]
153 153
154 [null]
155 155
156 [null]
157 157
158 [null]
159 159
16 [null]
160 [null]
161 161
162 [null]
163 163
164 [null]
165 165
166 [null]
167 167
168 [null]
169 169
17 17
170 [null]
171 171
172 [null]
173 173
174 [null]
175 175
176 [null]
177 177
178 [null]
179 179
18 [null]
180 [null]
181 181
182 [null]
183 183
184 [null]
185 185
186 [null]
187 187
188 [null]
189 189
19 19
190 [null]
191 191
192 [null]
193 193
194 [null]
195 195
196 [null]
197 197
198 [null]
199 199
2 [null]
20 [null]
200 [null]
201 201
202 [null]
203 203
204 [null]
205 205
206 [null]
207 207
208 [null]
209 209
21 21
210 [null]
211 211
212 [null]
213 213
214 [null]
215 215
216 [null]
217 217
218 [null]
219 219
22 [null]
220 [null]
221 221
222 [null]
223 223
224 [null]
225 225
226 [null]
227 227
228 [null]
229 229
23 23
230 [null]
231 231
232 [null]
233 233
234 [null]
235 235
236 [null]
237 237
238 [null]
239 239
24 [null]
240 [null]
241 241
242 [null]
243 243
244 [null]
245 245
246 [null]
247 247
248 [null]
249 249
25 25
250 [null]
251 251
252 [null]
253 253
254 [null]
255 255
26 [null]
27 27
28 [null]
29 29
3 3
30 [null]
31 31
32 [null]
33 33
34 [null]
35 35
36 [null]
37 37
38 [null]
39 39
4 [null]
40 [null]
41 41
42 [null]
43 43
44 [null]
45 45
46 [null]
47 47
48 [null]
49 49
5 5
50 [null]
51 51
52 [null]
53 53
54 [null]
55 55
56 [null]
57 57
58 [null]
59 59
6 [null]
60 [null]
61 61
62 [null]
63 63
64 [null]
65 65
66 [null]
67 67
68 [null]
69 69
7 7
70 [null]
71 71
72 [null]
73 73
74 [null]
75 75
76 [null]
77 77
78 [null]
79 79
8 [null]
80 [null]
81 81
82 [null]
83 83
84 [null]
85 85
86 [null]
87 87
88 [null]
89 89
9 9
90 [null]
91 91
92 [null]
93 93
94 [null]
95 95
96 [null]
97 97
98 [null]
99 99
MY
0 [null]
1 1
10 [null]
100 [null]
101 101
102 [null]
103 103
104 [null]
105 105
106 [null]
107 107
108 [null]
109 109
11 11
110 [null]
111 111
112 [null]
113 113
114 [null]
115 115
116 [null]
117 117
118 [null]
119 119
12 [null]
120 [null]
121 121
122 [null]
123 123
124 [null]
125 125
126 [null]
127 127
128 [null]
129 129
13 13
130 [null]
131 131
132 [null]
133 133
134 [null]
135 135
136 [null]
137 137
138 [null]
139 139
14 [null]
140 [null]
141 141
142 [null]
143 143
144 [null]
145 145
146 [null]
147 147
148 [null]
149 149
15 15
150 [null]
151 151
152 [null]
153 153
154 [null]
155 155
156 [null]
157 157
158 [null]
159 159
16 [null]
160 [null]
161 161
162 [null]
163 163
164 [null]
165 165
166 [null]
167 167
168 [null]
169 169
17 17
170 [null]
171 171
172 [null]
173 173
174 [null]
175 175
176 [null]
177 177
178 [null]
179 179
18 [null]
180 [null]
181 181
182 [null]
183 183
184 [null]
185 185
186 [null]
187 187
188 [null]
189 189
19 19
190 [null]
191 191
192 [null]
193 193
194 [null]
195 195
196 [null]
197 197
198 [null]
199 199
2 [null]
20 [null]
200 [null]
201 201
202 [null]
203 203
204 [null]
205 205
206 [null]
207 207
208 [null]
209 209
21 21
210 [null]
211 211
212 [null]
213 213
214 [null]
215 215
216 [null]
217 217
218 [null]
219 219
22 [null]
220 [null]
221 221
222 [null]
223 223
224 [null]
225 225
226 [null]
227 227
228 [null]
229 229
23 23
230 [null]
231 231
232 [null]
233 233
234 [null]
235 235
236 [null]
237 237
238 [null]
239 239
24 [null]
240 [null]
241 241
242 [null]
243 243
244 [null]
245 245
246 [null]
247 247
248 [null]
249 249
25 25
250 [null]
251 251
252 [null]
253 253
254 [null]
255 255
26 [null]
27 27
28 [null]
29 29
3 3
30 [null]
31 31
32 [null]
33 33
34 [null]
35 35
36 [null]
37 37
38 [null]
39 39
4 [null]
40 [null]
41 41
42 [null]
43 43
44 [null]
45 45
46 [null]
47 47
48 [null]
49 49
5 5
50 [null]
51 51
52 [null]
53 53
54 [null]
55 55
56 [null]
57 57
58 [null]
59 59
6 [null]
60 [null]
61 61
62 [null]
63 63
64 [null]
65 65
66 [null]
67 67
68 [null]
69 69
7 7
70 [null]
71 71
72 [null]
73 73
74 [null]
75 75
76 [null]
77 77
78 [null]
79 79
8 [null]
80 [null]
81 81
82 [null]
83 83
84 [null]
85 85
86 [null]
87 87
88 [null]
89 89
9 9
90 [null]
91 91
92 [null]
93 93
94 [null]
95 95
96 [null]
97 97
98 [null]
99 99
#!/usr/bin/perl
# vim:sw=2:ai
# test for binary cleanness (#3)
BEGIN {
push @INC, "../common/";
};
use strict;
use warnings;
use hstest;
my $dbh = hstest::init_testdb();
my $table = 'hstesttbl';
my $tablesize = 256;
$dbh->do(
"create table $table (k varchar(30) primary key, v varchar(30)) " .
"engine = innodb default charset = binary");
srand(999);
my %valmap = ();
print "WR\n";
my $sth = $dbh->prepare("insert into $table values (?,?)");
for (my $i = 0; $i < $tablesize; ++$i) {
my $k = "" . $i;
my $v = ($i % 2 == 1) ? $i : undef;
$sth->execute($k, $v);
$v = "[null]" if !defined($v);
print "$k $v\n";
$valmap{$k} = $v;
}
my $hs = hstest::get_hs_connection();
my $dbname = $hstest::conf{dbname};
$hs->open_index(1, $dbname, $table, '', 'k,v');
my $r = $hs->execute_single(1, '>=', [ '' ], 10000, 0);
shift(@$r);
print "HS\n";
for (my $i = 0; $i < $tablesize; ++$i) {
my $k = $r->[$i * 2];
my $v = $r->[$i * 2 + 1];
$v = "[null]" if !defined($v);
print "$k $v\n";
print "MISMATCH\n" if ($valmap{$k} ne $v);
}
undef $hs;
print "MY\n";
my $aref = $dbh->selectall_arrayref("select k,v from $table order by k");
for my $row (@$aref) {
my ($k, $v) = @$row;
$v = "[null]" if !defined($v);
print "$k $v\n";
print "MISMATCH\n" if ($valmap{$k} ne $v);
}
HSINSERTDUMP_TABLE
0 v1_0 v2_0
1 v1_1 v2_1
10 v1_10 v2_10
100 v1_100 v2_100
101 v1_101 v2_101
102 v1_102 v2_102
103 v1_103 v2_103
104 v1_104 v2_104
105 v1_105 v2_105
106 v1_106 v2_106
107 v1_107 v2_107
108 v1_108 v2_108
109 v1_109 v2_109
11 v1_11 v2_11
110 v1_110 v2_110
111 v1_111 v2_111
112 v1_112 v2_112
113 v1_113 v2_113
114 v1_114 v2_114
115 v1_115 v2_115
116 v1_116 v2_116
117 v1_117 v2_117
118 v1_118 v2_118
119 v1_119 v2_119
12 v1_12 v2_12
120 v1_120 v2_120
121 v1_121 v2_121
122 v1_122 v2_122
123 v1_123 v2_123
124 v1_124 v2_124
125 v1_125 v2_125
126 v1_126 v2_126
127 v1_127 v2_127
128 v1_128 v2_128
129 v1_129 v2_129
13 v1_13 v2_13
130 v1_130 v2_130
131 v1_131 v2_131
132 v1_132 v2_132
133 v1_133 v2_133
134 v1_134 v2_134
135 v1_135 v2_135
136 v1_136 v2_136
137 v1_137 v2_137
138 v1_138 v2_138
139 v1_139 v2_139
14 v1_14 v2_14
140 v1_140 v2_140
141 v1_141 v2_141
142 v1_142 v2_142
143 v1_143 v2_143
144 v1_144 v2_144
145 v1_145 v2_145
146 v1_146 v2_146
147 v1_147 v2_147
148 v1_148 v2_148
149 v1_149 v2_149
15 v1_15 v2_15
150 v1_150 v2_150
151 v1_151 v2_151
152 v1_152 v2_152
153 v1_153 v2_153
154 v1_154 v2_154
155 v1_155 v2_155
156 v1_156 v2_156
157 v1_157 v2_157
158 v1_158 v2_158
159 v1_159 v2_159
16 v1_16 v2_16
160 v1_160 v2_160
161 v1_161 v2_161
162 v1_162 v2_162
163 v1_163 v2_163
164 v1_164 v2_164
165 v1_165 v2_165
166 v1_166 v2_166
167 v1_167 v2_167
168 v1_168 v2_168
169 v1_169 v2_169
17 v1_17 v2_17
170 v1_170 v2_170
171 v1_171 v2_171
172 v1_172 v2_172
173 v1_173 v2_173
174 v1_174 v2_174
175 v1_175 v2_175
176 v1_176 v2_176
177 v1_177 v2_177
178 v1_178 v2_178
179 v1_179 v2_179
18 v1_18 v2_18
180 v1_180 v2_180
181 v1_181 v2_181
182 v1_182 v2_182
183 v1_183 v2_183
184 v1_184 v2_184
185 v1_185 v2_185
186 v1_186 v2_186
187 v1_187 v2_187
188 v1_188 v2_188
189 v1_189 v2_189
19 v1_19 v2_19
190 v1_190 v2_190
191 v1_191 v2_191
192 v1_192 v2_192
193 v1_193 v2_193
194 v1_194 v2_194
195 v1_195 v2_195
196 v1_196 v2_196
197 v1_197 v2_197
198 v1_198 v2_198
199 v1_199 v2_199
2 v1_2 v2_2
20 v1_20 v2_20
200 v1_200 v2_200
201 v1_201 v2_201
202 v1_202 v2_202
203 v1_203 v2_203
204 v1_204 v2_204
205 v1_205 v2_205
206 v1_206 v2_206
207 v1_207 v2_207
208 v1_208 v2_208
209 v1_209 v2_209
21 v1_21 v2_21
210 v1_210 v2_210
211 v1_211 v2_211
212 v1_212 v2_212
213 v1_213 v2_213
214 v1_214 v2_214
215 v1_215 v2_215
216 v1_216 v2_216
217 v1_217 v2_217
218 v1_218 v2_218
219 v1_219 v2_219
22 v1_22 v2_22
220 v1_220 v2_220
221 v1_221 v2_221
222 v1_222 v2_222
223 v1_223 v2_223
224 v1_224 v2_224
225 v1_225 v2_225
226 v1_226 v2_226
227 v1_227 v2_227
228 v1_228 v2_228
229 v1_229 v2_229
23 v1_23 v2_23
230 v1_230 v2_230
231 v1_231 v2_231
232 v1_232 v2_232
233 v1_233 v2_233
234 v1_234 v2_234
235 v1_235 v2_235
236 v1_236 v2_236
237 v1_237 v2_237
238 v1_238 v2_238
239 v1_239 v2_239
24 v1_24 v2_24
240 v1_240 v2_240
241 v1_241 v2_241
242 v1_242 v2_242
243 v1_243 v2_243
244 v1_244 v2_244
245 v1_245 v2_245
246 v1_246 v2_246
247 v1_247 v2_247
248 v1_248 v2_248
249 v1_249 v2_249
25 v1_25 v2_25
250 v1_250 v2_250
251 v1_251 v2_251
252 v1_252 v2_252
253 v1_253 v2_253
254 v1_254 v2_254
255 v1_255 v2_255
26 v1_26 v2_26
27 v1_27 v2_27
28 v1_28 v2_28
29 v1_29 v2_29
3 v1_3 v2_3
30 v1_30 v2_30
31 v1_31 v2_31
32 v1_32 v2_32
33 v1_33 v2_33
34 v1_34 v2_34
35 v1_35 v2_35
36 v1_36 v2_36
37 v1_37 v2_37
38 v1_38 v2_38
39 v1_39 v2_39
4 v1_4 v2_4
40 v1_40 v2_40
41 v1_41 v2_41
42 v1_42 v2_42
43 v1_43 v2_43
44 v1_44 v2_44
45 v1_45 v2_45
46 v1_46 v2_46
47 v1_47 v2_47
48 v1_48 v2_48
49 v1_49 v2_49
5 v1_5 v2_5
50 v1_50 v2_50
51 v1_51 v2_51
52 v1_52 v2_52
53 v1_53 v2_53
54 v1_54 v2_54
55 v1_55 v2_55
56 v1_56 v2_56
57 v1_57 v2_57
58 v1_58 v2_58
59 v1_59 v2_59
6 v1_6 v2_6
60 v1_60 v2_60
61 v1_61 v2_61
62 v1_62 v2_62
63 v1_63 v2_63
64 v1_64 v2_64
65 v1_65 v2_65
66 v1_66 v2_66
67 v1_67 v2_67
68 v1_68 v2_68
69 v1_69 v2_69
7 v1_7 v2_7
70 v1_70 v2_70
71 v1_71 v2_71
72 v1_72 v2_72
73 v1_73 v2_73
74 v1_74 v2_74
75 v1_75 v2_75
76 v1_76 v2_76
77 v1_77 v2_77
78 v1_78 v2_78
79 v1_79 v2_79
8 v1_8 v2_8
80 v1_80 v2_80
81 v1_81 v2_81
82 v1_82 v2_82
83 v1_83 v2_83
84 v1_84 v2_84
85 v1_85 v2_85
86 v1_86 v2_86
87 v1_87 v2_87
88 v1_88 v2_88
89 v1_89 v2_89
9 v1_9 v2_9
90 v1_90 v2_90
91 v1_91 v2_91
92 v1_92 v2_92
93 v1_93 v2_93
94 v1_94 v2_94
95 v1_95 v2_95
96 v1_96 v2_96
97 v1_97 v2_97
98 v1_98 v2_98
99 v1_99 v2_99
HSUPDATEDUMP_TABLE
0 mod_0 v2_0
1 mod_1 v2_1
10 mod_10 v2_10
100 mod_100 v2_100
101 mod_101 v2_101
102 mod_102 v2_102
103 mod_103 v2_103
104 mod_104 v2_104
105 mod_105 v2_105
106 mod_106 v2_106
107 mod_107 v2_107
108 mod_108 v2_108
109 mod_109 v2_109
11 mod_11 v2_11
110 mod_110 v2_110
111 mod_111 v2_111
112 mod_112 v2_112
113 mod_113 v2_113
114 mod_114 v2_114
115 mod_115 v2_115
116 mod_116 v2_116
117 mod_117 v2_117
118 mod_118 v2_118
119 mod_119 v2_119
12 mod_12 v2_12
120 mod_120 v2_120
121 mod_121 v2_121
122 mod_122 v2_122
123 mod_123 v2_123
124 mod_124 v2_124
125 mod_125 v2_125
126 mod_126 v2_126
127 mod_127 v2_127
128 mod_128 v2_128
129 mod_129 v2_129
13 mod_13 v2_13
130 mod_130 v2_130
131 mod_131 v2_131
132 mod_132 v2_132
133 mod_133 v2_133
134 mod_134 v2_134
135 mod_135 v2_135
136 mod_136 v2_136
137 mod_137 v2_137
138 mod_138 v2_138
139 mod_139 v2_139
14 mod_14 v2_14
140 mod_140 v2_140
141 mod_141 v2_141
142 mod_142 v2_142
143 mod_143 v2_143
144 mod_144 v2_144
145 mod_145 v2_145
146 mod_146 v2_146
147 mod_147 v2_147
148 mod_148 v2_148
149 mod_149 v2_149
15 mod_15 v2_15
150 mod_150 v2_150
151 mod_151 v2_151
152 mod_152 v2_152
153 mod_153 v2_153
154 mod_154 v2_154
155 mod_155 v2_155
156 mod_156 v2_156
157 mod_157 v2_157
158 mod_158 v2_158
159 mod_159 v2_159
16 mod_16 v2_16
160 mod_160 v2_160
161 mod_161 v2_161
162 mod_162 v2_162
163 mod_163 v2_163
164 mod_164 v2_164
165 mod_165 v2_165
166 mod_166 v2_166
167 mod_167 v2_167
168 mod_168 v2_168
169 mod_169 v2_169
17 mod_17 v2_17
170 mod_170 v2_170
171 mod_171 v2_171
172 mod_172 v2_172
173 mod_173 v2_173
174 mod_174 v2_174
175 mod_175 v2_175
176 mod_176 v2_176
177 mod_177 v2_177
178 mod_178 v2_178
179 mod_179 v2_179
18 mod_18 v2_18
180 mod_180 v2_180
181 mod_181 v2_181
182 mod_182 v2_182
183 mod_183 v2_183
184 mod_184 v2_184
185 mod_185 v2_185
186 mod_186 v2_186
187 mod_187 v2_187
188 mod_188 v2_188
189 mod_189 v2_189
19 mod_19 v2_19
190 mod_190 v2_190
191 mod_191 v2_191
192 mod_192 v2_192
193 mod_193 v2_193
194 mod_194 v2_194
195 mod_195 v2_195
196 mod_196 v2_196
197 mod_197 v2_197
198 mod_198 v2_198
199 mod_199 v2_199
2 mod_2 v2_2
20 mod_20 v2_20
200 mod_200 v2_200
201 mod_201 v2_201
202 mod_202 v2_202
203 mod_203 v2_203
204 mod_204 v2_204
205 mod_205 v2_205
206 mod_206 v2_206
207 mod_207 v2_207
208 mod_208 v2_208
209 mod_209 v2_209
21 mod_21 v2_21
210 mod_210 v2_210
211 mod_211 v2_211
212 mod_212 v2_212
213 mod_213 v2_213
214 mod_214 v2_214
215 mod_215 v2_215
216 mod_216 v2_216
217 mod_217 v2_217
218 mod_218 v2_218
219 mod_219 v2_219
22 mod_22 v2_22
220 mod_220 v2_220
221 mod_221 v2_221
222 mod_222 v2_222
223 mod_223 v2_223
224 mod_224 v2_224
225 mod_225 v2_225
226 mod_226 v2_226
227 mod_227 v2_227
228 mod_228 v2_228
229 mod_229 v2_229
23 mod_23 v2_23
230 mod_230 v2_230
231 mod_231 v2_231
232 mod_232 v2_232
233 mod_233 v2_233
234 mod_234 v2_234
235 mod_235 v2_235
236 mod_236 v2_236
237 mod_237 v2_237
238 mod_238 v2_238
239 mod_239 v2_239
24 mod_24 v2_24
240 mod_240 v2_240
241 mod_241 v2_241
242 mod_242 v2_242
243 mod_243 v2_243
244 mod_244 v2_244
245 mod_245 v2_245
246 mod_246 v2_246
247 mod_247 v2_247
248 mod_248 v2_248
249 mod_249 v2_249
25 mod_25 v2_25
250 mod_250 v2_250
251 mod_251 v2_251
252 mod_252 v2_252
253 mod_253 v2_253
254 mod_254 v2_254
255 mod_255 v2_255
26 mod_26 v2_26
27 mod_27 v2_27
28 mod_28 v2_28
29 mod_29 v2_29
3 mod_3 v2_3
30 mod_30 v2_30
31 mod_31 v2_31
32 mod_32 v2_32
33 mod_33 v2_33
34 mod_34 v2_34
35 mod_35 v2_35
36 mod_36 v2_36
37 mod_37 v2_37
38 mod_38 v2_38
39 mod_39 v2_39
4 mod_4 v2_4
40 mod_40 v2_40
41 mod_41 v2_41
42 mod_42 v2_42
43 mod_43 v2_43
44 mod_44 v2_44
45 mod_45 v2_45
46 mod_46 v2_46
47 mod_47 v2_47
48 mod_48 v2_48
49 mod_49 v2_49
5 mod_5 v2_5
50 mod_50 v2_50
51 mod_51 v2_51
52 mod_52 v2_52
53 mod_53 v2_53
54 mod_54 v2_54
55 mod_55 v2_55
56 mod_56 v2_56
57 mod_57 v2_57
58 mod_58 v2_58
59 mod_59 v2_59
6 mod_6 v2_6
60 mod_60 v2_60
61 mod_61 v2_61
62 mod_62 v2_62
63 mod_63 v2_63
64 mod_64 v2_64
65 mod_65 v2_65
66 mod_66 v2_66
67 mod_67 v2_67
68 mod_68 v2_68
69 mod_69 v2_69
7 mod_7 v2_7
70 mod_70 v2_70
71 mod_71 v2_71
72 mod_72 v2_72
73 mod_73 v2_73
74 mod_74 v2_74
75 mod_75 v2_75
76 mod_76 v2_76
77 mod_77 v2_77
78 mod_78 v2_78
79 mod_79 v2_79
8 mod_8 v2_8
80 mod_80 v2_80
81 mod_81 v2_81
82 mod_82 v2_82
83 mod_83 v2_83
84 mod_84 v2_84
85 mod_85 v2_85
86 mod_86 v2_86
87 mod_87 v2_87
88 mod_88 v2_88
89 mod_89 v2_89
9 mod_9 v2_9
90 mod_90 v2_90
91 mod_91 v2_91
92 mod_92 v2_92
93 mod_93 v2_93
94 mod_94 v2_94
95 mod_95 v2_95
96 mod_96 v2_96
97 mod_97 v2_97
98 mod_98 v2_98
99 mod_99 v2_99
HSDELETE
DUMP_TABLE
1 mod_1 v2_1
101 mod_101 v2_101
103 mod_103 v2_103
105 mod_105 v2_105
107 mod_107 v2_107
109 mod_109 v2_109
11 mod_11 v2_11
111 mod_111 v2_111
113 mod_113 v2_113
115 mod_115 v2_115
117 mod_117 v2_117
119 mod_119 v2_119
121 mod_121 v2_121
123 mod_123 v2_123
125 mod_125 v2_125
127 mod_127 v2_127
129 mod_129 v2_129
13 mod_13 v2_13
131 mod_131 v2_131
133 mod_133 v2_133
135 mod_135 v2_135
137 mod_137 v2_137
139 mod_139 v2_139
141 mod_141 v2_141
143 mod_143 v2_143
145 mod_145 v2_145
147 mod_147 v2_147
149 mod_149 v2_149
15 mod_15 v2_15
151 mod_151 v2_151
153 mod_153 v2_153
155 mod_155 v2_155
157 mod_157 v2_157
159 mod_159 v2_159
161 mod_161 v2_161
163 mod_163 v2_163
165 mod_165 v2_165
167 mod_167 v2_167
169 mod_169 v2_169
17 mod_17 v2_17
171 mod_171 v2_171
173 mod_173 v2_173
175 mod_175 v2_175
177 mod_177 v2_177
179 mod_179 v2_179
181 mod_181 v2_181
183 mod_183 v2_183
185 mod_185 v2_185
187 mod_187 v2_187
189 mod_189 v2_189
19 mod_19 v2_19
191 mod_191 v2_191
193 mod_193 v2_193
195 mod_195 v2_195
197 mod_197 v2_197
199 mod_199 v2_199
201 mod_201 v2_201
203 mod_203 v2_203
205 mod_205 v2_205
207 mod_207 v2_207
209 mod_209 v2_209
21 mod_21 v2_21
211 mod_211 v2_211
213 mod_213 v2_213
215 mod_215 v2_215
217 mod_217 v2_217
219 mod_219 v2_219
221 mod_221 v2_221
223 mod_223 v2_223
225 mod_225 v2_225
227 mod_227 v2_227
229 mod_229 v2_229
23 mod_23 v2_23
231 mod_231 v2_231
233 mod_233 v2_233
235 mod_235 v2_235
237 mod_237 v2_237
239 mod_239 v2_239
241 mod_241 v2_241
243 mod_243 v2_243
245 mod_245 v2_245
247 mod_247 v2_247
249 mod_249 v2_249
25 mod_25 v2_25
251 mod_251 v2_251
253 mod_253 v2_253
255 mod_255 v2_255
27 mod_27 v2_27
29 mod_29 v2_29
3 mod_3 v2_3
31 mod_31 v2_31
33 mod_33 v2_33
35 mod_35 v2_35
37 mod_37 v2_37
39 mod_39 v2_39
41 mod_41 v2_41
43 mod_43 v2_43
45 mod_45 v2_45
47 mod_47 v2_47
49 mod_49 v2_49
5 mod_5 v2_5
51 mod_51 v2_51
53 mod_53 v2_53
55 mod_55 v2_55
57 mod_57 v2_57
59 mod_59 v2_59
61 mod_61 v2_61
63 mod_63 v2_63
65 mod_65 v2_65
67 mod_67 v2_67
69 mod_69 v2_69
7 mod_7 v2_7
71 mod_71 v2_71
73 mod_73 v2_73
75 mod_75 v2_75
77 mod_77 v2_77
79 mod_79 v2_79
81 mod_81 v2_81
83 mod_83 v2_83
85 mod_85 v2_85
87 mod_87 v2_87
89 mod_89 v2_89
9 mod_9 v2_9
91 mod_91 v2_91
93 mod_93 v2_93
95 mod_95 v2_95
97 mod_97 v2_97
99 mod_99 v2_99
#!/usr/bin/perl
# vim:sw=2:ai
# test for insert/update/delete
BEGIN {
push @INC, "../common/";
};
use strict;
use warnings;
use hstest;
my $dbh = hstest::init_testdb();
my $table = 'hstesttbl';
my $tablesize = 256;
$dbh->do(
"create table $table (" .
"k varchar(30) primary key, " .
"v1 varchar(30), " .
"v2 varchar(30)) " .
"engine = innodb default charset = binary");
srand(999);
my %valmap = ();
print "HSINSERT";
my $hs = hstest::get_hs_connection(undef, 9999);
my $dbname = $hstest::conf{dbname};
$hs->open_index(1, $dbname, $table, '', 'k,v1,v2');
for (my $i = 0; $i < $tablesize; ++$i) {
my $k = "" . $i;
my $v1 = "v1_" . $i;
my $v2 = "v2_" . $i;
my $r = $hs->execute_insert(1, [ $k, $v1, $v2 ]);
my $err = $r->[0];
if ($err != 0) {
my $err_str = $r->[1];
print "$err $err_str\n";
}
}
undef $hs;
dump_table();
print "HSUPDATE";
$hs = hstest::get_hs_connection(undef, 9999);
$dbname = $hstest::conf{dbname};
$hs->open_index(2, $dbname, $table, '', 'v1');
for (my $i = 0; $i < $tablesize; ++$i) {
my $r = $hs->execute_single(2, '=', [ $i ], 1000, 0, 'U', [ "mod_$i" ]);
my $err = $r->[0];
if ($err != 0) {
my $err_str = $r->[1];
print "$err $err_str\n";
}
}
undef $hs;
dump_table();
print "HSDELETE\n";
$hs = hstest::get_hs_connection(undef, 9999);
$dbname = $hstest::conf{dbname};
$hs->open_index(3, $dbname, $table, '', '');
for (my $i = 0; $i < $tablesize; $i = $i + 2) {
my $r = $hs->execute_single(3, '=', [ $i ], 1000, 0, 'D');
my $err = $r->[0];
if ($err != 0) {
my $err_str = $r->[1];
print "$err $err_str\n";
}
}
undef $hs;
dump_table();
sub dump_table {
print "DUMP_TABLE\n";
my $aref = $dbh->selectall_arrayref("select k,v1,v2 from $table order by k");
for my $row (@$aref) {
my ($k, $v1, $v2) = @$row;
$v1 = "[null]" if !defined($v1);
$v2 = "[null]" if !defined($v2);
print "$k $v1 $v2\n";
# print "MISMATCH\n" if ($valmap{$k} ne $v);
}
}
MY
0 1v1020 2v6350
1 1v8031 2v9251
2 1v7752 2v5372
3 [NULL] 2v4143
4 1v5904 2v3024
5 1v7045 2v7515
6 1v3676 2v4006
7 1v3977 2v1707
8 1v7198 2v7348
9 1v5879 2v4949
10 1v52310 2v95410
11 1v43311 2v82011
12 1v28312 2v83712
13 [NULL] 2v41513
14 1v54514 2v58314
15 1v5215 2v32315
16 1v61416 2v67916
17 1v80517 2v45117
18 1v11518 2v26918
19 1v21819 2v61719
20 1v87820 2v34520
21 1v51221 2v96921
22 1v40822 2v29122
23 [NULL] 2v95323
24 1v71024 2v14224
25 1v68225 2v93425
26 1v62126 2v96526
27 1v57427 2v20427
28 1v29828 2v13428
29 1v98329 2v44429
30 1v14430 2v15230
31 1v18731 2v21531
32 1v832 2v69732
33 [NULL] 2v28033
34 1v70134 2v53734
35 1v41335 2v6935
36 1v8636 2v82236
37 1v67037 2v37037
38 1v80638 2v68838
39 1v2639 2v6639
40 1v80240 2v17140
41 1v55741 2v84741
42 1v77742 2v73042
43 [NULL] 2v11543
44 1v64644 2v49644
45 1v12045 2v68445
46 1v37446 2v6546
47 1v37047 2v17447
48 1v82848 2v86748
49 1v75949 2v70349
50 1v84350 2v94250
51 1v40151 2v36251
52 1v36752 2v30752
53 [NULL] 2v16753
54 1v79954 2v82954
55 1v53955 2v37955
56 1v56056 2v85856
57 1v57957 2v2657
58 1v88758 2v50758
59 1v34559 2v89859
60 1v43060 2v80160
61 1v84561 2v32561
62 1v62462 2v48062
63 [NULL] 2v67663
64 1v5364 2v73664
65 1v80965 2v27065
66 1v17566 2v83966
67 1v6167 2v22267
68 1v35868 2v50568
69 1v62769 2v90669
70 1v4670 2v98170
71 1v11971 2v471
72 1v1472 2v48072
73 [NULL] 2v40573
74 1v87574 2v63974
75 1v82775 2v34475
76 1v59876 2v56376
77 1v77077 2v51677
78 1v53878 2v54878
79 1v35779 2v32279
80 1v3680 2v37080
81 1v33181 2v81581
82 1v76982 2v66882
83 [NULL] 2v28183
84 1v69684 2v45284
85 1v40685 2v185
86 1v39586 2v32486
87 1v3687 2v73887
88 1v16088 2v7988
89 1v75989 2v65789
90 1v3190 2v78390
91 1v65091 2v82491
92 1v85292 2v6892
93 [NULL] 2v54693
94 1v81394 2v77594
95 1v8795 2v69695
96 1v19696 2v38096
97 1v7997 2v75197
98 1v32398 2v21798
99 1v3799 2v70199
HS
0 1v1020 2v6350
1 1v8031 2v9251
2 1v7752 2v5372
3 [NULL] 2v4143
4 1v5904 2v3024
5 1v7045 2v7515
6 1v3676 2v4006
7 1v3977 2v1707
8 1v7198 2v7348
9 1v5879 2v4949
10 1v52310 2v95410
11 1v43311 2v82011
12 1v28312 2v83712
13 [NULL] 2v41513
14 1v54514 2v58314
15 1v5215 2v32315
16 1v61416 2v67916
17 1v80517 2v45117
18 1v11518 2v26918
19 1v21819 2v61719
20 1v87820 2v34520
21 1v51221 2v96921
22 1v40822 2v29122
23 [NULL] 2v95323
24 1v71024 2v14224
25 1v68225 2v93425
26 1v62126 2v96526
27 1v57427 2v20427
28 1v29828 2v13428
29 1v98329 2v44429
30 1v14430 2v15230
31 1v18731 2v21531
32 1v832 2v69732
33 [NULL] 2v28033
34 1v70134 2v53734
35 1v41335 2v6935
36 1v8636 2v82236
37 1v67037 2v37037
38 1v80638 2v68838
39 1v2639 2v6639
40 1v80240 2v17140
41 1v55741 2v84741
42 1v77742 2v73042
43 [NULL] 2v11543
44 1v64644 2v49644
45 1v12045 2v68445
46 1v37446 2v6546
47 1v37047 2v17447
48 1v82848 2v86748
49 1v75949 2v70349
50 1v84350 2v94250
51 1v40151 2v36251
52 1v36752 2v30752
53 [NULL] 2v16753
54 1v79954 2v82954
55 1v53955 2v37955
56 1v56056 2v85856
57 1v57957 2v2657
58 1v88758 2v50758
59 1v34559 2v89859
60 1v43060 2v80160
61 1v84561 2v32561
62 1v62462 2v48062
63 [NULL] 2v67663
64 1v5364 2v73664
65 1v80965 2v27065
66 1v17566 2v83966
67 1v6167 2v22267
68 1v35868 2v50568
69 1v62769 2v90669
70 1v4670 2v98170
71 1v11971 2v471
72 1v1472 2v48072
73 [NULL] 2v40573
74 1v87574 2v63974
75 1v82775 2v34475
76 1v59876 2v56376
77 1v77077 2v51677
78 1v53878 2v54878
79 1v35779 2v32279
80 1v3680 2v37080
81 1v33181 2v81581
82 1v76982 2v66882
83 [NULL] 2v28183
84 1v69684 2v45284
85 1v40685 2v185
86 1v39586 2v32486
87 1v3687 2v73887
88 1v16088 2v7988
89 1v75989 2v65789
90 1v3190 2v78390
91 1v65091 2v82491
92 1v85292 2v6892
93 [NULL] 2v54693
94 1v81394 2v77594
95 1v8795 2v69695
96 1v19696 2v38096
97 1v7997 2v75197
98 1v32398 2v21798
99 1v3799 2v70199
2ndIDX
2ndidx 0 1v1020 => 0 1v1020 2v6350
2ndidx 1 1v8031 => 1 1v8031 2v9251
2ndidx 2 1v7752 => 2 1v7752 2v5372
2ndidx 4 1v5904 => 4 1v5904 2v3024
2ndidx 5 1v7045 => 5 1v7045 2v7515
2ndidx 6 1v3676 => 6 1v3676 2v4006
2ndidx 7 1v3977 => 7 1v3977 2v1707
2ndidx 8 1v7198 => 8 1v7198 2v7348
2ndidx 9 1v5879 => 9 1v5879 2v4949
2ndidx 10 1v52310 => 10 1v52310 2v95410
2ndidx 11 1v43311 => 11 1v43311 2v82011
2ndidx 12 1v28312 => 12 1v28312 2v83712
2ndidx 14 1v54514 => 14 1v54514 2v58314
2ndidx 15 1v5215 => 15 1v5215 2v32315
2ndidx 16 1v61416 => 16 1v61416 2v67916
2ndidx 17 1v80517 => 17 1v80517 2v45117
2ndidx 18 1v11518 => 18 1v11518 2v26918
2ndidx 19 1v21819 => 19 1v21819 2v61719
2ndidx 20 1v87820 => 20 1v87820 2v34520
2ndidx 21 1v51221 => 21 1v51221 2v96921
2ndidx 22 1v40822 => 22 1v40822 2v29122
2ndidx 24 1v71024 => 24 1v71024 2v14224
2ndidx 25 1v68225 => 25 1v68225 2v93425
2ndidx 26 1v62126 => 26 1v62126 2v96526
2ndidx 27 1v57427 => 27 1v57427 2v20427
2ndidx 28 1v29828 => 28 1v29828 2v13428
2ndidx 29 1v98329 => 29 1v98329 2v44429
2ndidx 30 1v14430 => 30 1v14430 2v15230
2ndidx 31 1v18731 => 31 1v18731 2v21531
2ndidx 32 1v832 => 32 1v832 2v69732
2ndidx 34 1v70134 => 34 1v70134 2v53734
2ndidx 35 1v41335 => 35 1v41335 2v6935
2ndidx 36 1v8636 => 36 1v8636 2v82236
2ndidx 37 1v67037 => 37 1v67037 2v37037
2ndidx 38 1v80638 => 38 1v80638 2v68838
2ndidx 39 1v2639 => 39 1v2639 2v6639
2ndidx 40 1v80240 => 40 1v80240 2v17140
2ndidx 41 1v55741 => 41 1v55741 2v84741
2ndidx 42 1v77742 => 42 1v77742 2v73042
2ndidx 44 1v64644 => 44 1v64644 2v49644
2ndidx 45 1v12045 => 45 1v12045 2v68445
2ndidx 46 1v37446 => 46 1v37446 2v6546
2ndidx 47 1v37047 => 47 1v37047 2v17447
2ndidx 48 1v82848 => 48 1v82848 2v86748
2ndidx 49 1v75949 => 49 1v75949 2v70349
2ndidx 50 1v84350 => 50 1v84350 2v94250
2ndidx 51 1v40151 => 51 1v40151 2v36251
2ndidx 52 1v36752 => 52 1v36752 2v30752
2ndidx 54 1v79954 => 54 1v79954 2v82954
2ndidx 55 1v53955 => 55 1v53955 2v37955
2ndidx 56 1v56056 => 56 1v56056 2v85856
2ndidx 57 1v57957 => 57 1v57957 2v2657
2ndidx 58 1v88758 => 58 1v88758 2v50758
2ndidx 59 1v34559 => 59 1v34559 2v89859
2ndidx 60 1v43060 => 60 1v43060 2v80160
2ndidx 61 1v84561 => 61 1v84561 2v32561
2ndidx 62 1v62462 => 62 1v62462 2v48062
2ndidx 64 1v5364 => 64 1v5364 2v73664
2ndidx 65 1v80965 => 65 1v80965 2v27065
2ndidx 66 1v17566 => 66 1v17566 2v83966
2ndidx 67 1v6167 => 67 1v6167 2v22267
2ndidx 68 1v35868 => 68 1v35868 2v50568
2ndidx 69 1v62769 => 69 1v62769 2v90669
2ndidx 70 1v4670 => 70 1v4670 2v98170
2ndidx 71 1v11971 => 71 1v11971 2v471
2ndidx 72 1v1472 => 72 1v1472 2v48072
2ndidx 74 1v87574 => 74 1v87574 2v63974
2ndidx 75 1v82775 => 75 1v82775 2v34475
2ndidx 76 1v59876 => 76 1v59876 2v56376
2ndidx 77 1v77077 => 77 1v77077 2v51677
2ndidx 78 1v53878 => 78 1v53878 2v54878
2ndidx 79 1v35779 => 79 1v35779 2v32279
2ndidx 80 1v3680 => 80 1v3680 2v37080
2ndidx 81 1v33181 => 81 1v33181 2v81581
2ndidx 82 1v76982 => 82 1v76982 2v66882
2ndidx 84 1v69684 => 84 1v69684 2v45284
2ndidx 85 1v40685 => 85 1v40685 2v185
2ndidx 86 1v39586 => 86 1v39586 2v32486
2ndidx 87 1v3687 => 87 1v3687 2v73887
2ndidx 88 1v16088 => 88 1v16088 2v7988
2ndidx 89 1v75989 => 89 1v75989 2v65789
2ndidx 90 1v3190 => 90 1v3190 2v78390
2ndidx 91 1v65091 => 91 1v65091 2v82491
2ndidx 92 1v85292 => 92 1v85292 2v6892
2ndidx 94 1v81394 => 94 1v81394 2v77594
2ndidx 95 1v8795 => 95 1v8795 2v69695
2ndidx 96 1v19696 => 96 1v19696 2v38096
2ndidx 97 1v7997 => 97 1v7997 2v75197
2ndidx 98 1v32398 => 98 1v32398 2v21798
2ndidx 99 1v3799 => 99 1v3799 2v70199
2ndIDX NULL
2ndidxnull 3 2v4143
2ndidxnull 13 2v41513
2ndidxnull 23 2v95323
2ndidxnull 33 2v28033
2ndidxnull 43 2v11543
2ndidxnull 53 2v16753
2ndidxnull 63 2v67663
2ndidxnull 73 2v40573
2ndidxnull 83 2v28183
2ndidxnull 93 2v54693
#!/usr/bin/perl
# vim:sw=2:ai
# test for nulls
BEGIN {
push @INC, "../common/";
};
# use strict;
use warnings;
use hstest;
my $dbh = hstest::init_testdb();
my $table = 'hstesttbl';
my $tablesize = 100;
$dbh->do(
"create table $table (" .
"k int primary key, v1 varchar(30), v2 varchar(30), " .
"key idxv1 (v1) " .
") engine = innodb");
srand(999);
my %valmap = ();
my $sth = $dbh->prepare("insert into $table values (?,?,?)");
for (my $i = 0; $i < $tablesize; ++$i) {
my $k = "" . $i;
my $v1 = "1v" . int(rand(1000)) . $i;
my $v2 = "2v" . int(rand(1000)) . $i;
if ($i % 10 == 3) {
$v1 = undef;
}
$sth->execute($k, $v1, $v2);
$valmap{$k} = $v1;
}
print "MY\n";
my $aref = $dbh->selectall_arrayref("select k,v1,v2 from $table order by k");
for my $row (@$aref) {
my ($k, $v1, $v2) = @$row;
$v1 = "[NULL]" if (!defined($v1));
print "$k $v1 $v2\n";
}
print "HS\n";
my $hs = hstest::get_hs_connection();
my $dbname = $hstest::conf{dbname};
$hs->open_index(1, $dbname, $table, '', 'k,v1,v2');
my $r = $hs->execute_single(1, '>=', [ '' ], 10000, 0);
shift(@$r);
for (my $i = 0; $i < $tablesize; ++$i) {
my $k = $r->[$i * 3];
my $v1 = $r->[$i * 3 + 1];
my $v2 = $r->[$i * 3 + 2];
$v1 = "[NULL]" if (!defined($v1));
print "$k $v1 $v2\n";
}
print "2ndIDX\n";
$hs->open_index(2, $dbname, $table, 'idxv1', 'k,v1,v2');
for (my $i = 0; $i < $tablesize; ++$i) {
my $k = "" . $i;
my $v1 = $valmap{$k};
next if !defined($v1);
my $r = $hs->execute_single(2, '=', [ $v1 ], 1, 0);
shift(@$r);
my $r_k = $r->[0];
my $r_v1 = $r->[1];
my $r_v2 = $r->[2];
print "2ndidx $k $v1 => $r_k $r_v1 $r_v2\n";
}
print "2ndIDX NULL\n";
{
my %rvals = ();
my $v1 = undef;
my @arr;
push(@arr, undef);
my $kv = \@arr;
my $r = $hs->execute_single(2, "=", $kv, 10000, 0);
shift(@$r);
for (my $i = 0; $i < scalar(@$r); $i += 3) {
my $k = $r->[$i];
my $v1 = $r->[$i + 1];
my $v2 = $r->[$i + 2];
$rvals{$k} = [ $k, $v1, $v2 ];
}
for my $i (sort { $a <=> $b } keys %rvals) {
my $rec = $rvals{$i};
my $k = $rec->[0];
my $v1 = $rec->[1];
my $v2 = $rec->[2];
print "2ndidxnull $k $v2\n";
}
}
#!/usr/bin/perl
# vim:sw=2:ai
# test for not-found
BEGIN {
push @INC, "../common/";
};
use strict;
use warnings;
use hstest;
my $dbh = hstest::init_testdb();
my $table = 'hstesttbl';
my $tablesize = 100;
$dbh->do(
"create table $table (k varchar(30) primary key, v varchar(30) not null) " .
"engine = innodb");
srand(999);
my %valmap = ();
my $sth = $dbh->prepare("insert into $table values (?,?)");
for (my $i = 0; $i < $tablesize; ++$i) {
my $k = "k" . $i;
my $v = "v" . int(rand(1000)) . $i;
$sth->execute($k, $v);
$valmap{$k} = $v;
}
my $hs = hstest::get_hs_connection();
my $dbname = $hstest::conf{dbname};
$hs->open_index(1, $dbname, $table, '', 'k,v');
dump_rec($hs, 1, 'k5'); # found
dump_rec($hs, 1, 'k000000'); # notfound
sub dump_rec {
my ($hs, $idxid, $key) = @_;
my $r = $hs->execute_single($idxid, '=', [ $key ], 1, 0);
for my $fld (@$r) {
print "[$fld]";
}
print "\n";
}
DEL
[0][1]
[0][k50][v68250][k51][v93451]
DELINS
[0][k6][v5926][k60][v14460][k61][v15261]
[0][1]
[0]
[0][k6][v5926][k60][INS][k61][v15261]
DELUPUP
[0][k7][v4147][k70][v41370][k71][v6971]
[0][1]
[0][k7][v4147][k70][UP][k71][v6971]
#!/usr/bin/perl
# vim:sw=2:ai
# test for multiple modify requests
BEGIN {
push @INC, "../common/";
};
use strict;
use warnings;
use hstest;
my $dbh = hstest::init_testdb();
my $table = 'hstesttbl';
my $tablesize = 100;
$dbh->do(
"create table $table (k varchar(30) primary key, v varchar(30) not null) " .
"engine = innodb");
srand(999);
my %valmap = ();
my $sth = $dbh->prepare("insert into $table values (?,?)");
for (my $i = 0; $i < $tablesize; ++$i) {
my $k = "k" . $i;
my $v = "v" . int(rand(1000)) . $i;
$sth->execute($k, $v);
$valmap{$k} = $v;
}
my $hs = hstest::get_hs_connection(undef, 9999);
my $dbname = $hstest::conf{dbname};
$hs->open_index(1, $dbname, $table, '', 'k,v');
exec_multi(
"DEL",
[ 1, '=', [ 'k5' ], 1, 0, 'D' ],
[ 1, '>=', [ 'k5' ], 2, 0 ],
);
exec_multi(
"DELINS",
[ 1, '>=', [ 'k6' ], 3, 0 ],
[ 1, '=', [ 'k60' ], 1, 0, 'D' ],
[ 1, '+', [ 'k60', 'INS' ] ],
[ 1, '>=', [ 'k6' ], 3, 0 ],
);
exec_multi(
"DELUPUP",
[ 1, '>=', [ 'k7' ], 3, 0 ],
[ 1, '=', [ 'k70' ], 1, 0, 'U', [ 'k70', 'UP' ] ],
[ 1, '>=', [ 'k7' ], 3, 0 ],
);
sub exec_multi {
my $mess = shift(@_);
print "$mess\n";
my $mres = $hs->execute_multi(\@_);
for my $res (@$mres) {
for my $fld (@$res) {
print "[$fld]";
}
print "\n";
}
}
HSINSERTDUMP_TABLE
0 v1_0 [null]
1 v1_1 [null]
10 v1_10 [null]
100 v1_100 [null]
101 v1_101 [null]
102 v1_102 [null]
103 v1_103 [null]
104 v1_104 [null]
105 v1_105 [null]
106 v1_106 [null]
107 v1_107 [null]
108 v1_108 [null]
109 v1_109 [null]
11 v1_11 [null]
110 v1_110 [null]
111 v1_111 [null]
112 v1_112 [null]
113 v1_113 [null]
114 v1_114 [null]
115 v1_115 [null]
116 v1_116 [null]
117 v1_117 [null]
118 v1_118 [null]
119 v1_119 [null]
12 v1_12 [null]
120 v1_120 [null]
121 v1_121 [null]
122 v1_122 [null]
123 v1_123 [null]
124 v1_124 [null]
125 v1_125 [null]
126 v1_126 [null]
127 v1_127 [null]
128 v1_128 [null]
129 v1_129 [null]
13 v1_13 [null]
130 v1_130 [null]
131 v1_131 [null]
132 v1_132 [null]
133 v1_133 [null]
134 v1_134 [null]
135 v1_135 [null]
136 v1_136 [null]
137 v1_137 [null]
138 v1_138 [null]
139 v1_139 [null]
14 v1_14 [null]
140 v1_140 [null]
141 v1_141 [null]
142 v1_142 [null]
143 v1_143 [null]
144 v1_144 [null]
145 v1_145 [null]
146 v1_146 [null]
147 v1_147 [null]
148 v1_148 [null]
149 v1_149 [null]
15 v1_15 [null]
150 v1_150 [null]
151 v1_151 [null]
152 v1_152 [null]
153 v1_153 [null]
154 v1_154 [null]
155 v1_155 [null]
156 v1_156 [null]
157 v1_157 [null]
158 v1_158 [null]
159 v1_159 [null]
16 v1_16 [null]
160 v1_160 [null]
161 v1_161 [null]
162 v1_162 [null]
163 v1_163 [null]
164 v1_164 [null]
165 v1_165 [null]
166 v1_166 [null]
167 v1_167 [null]
168 v1_168 [null]
169 v1_169 [null]
17 v1_17 [null]
170 v1_170 [null]
171 v1_171 [null]
172 v1_172 [null]
173 v1_173 [null]
174 v1_174 [null]
175 v1_175 [null]
176 v1_176 [null]
177 v1_177 [null]
178 v1_178 [null]
179 v1_179 [null]
18 v1_18 [null]
180 v1_180 [null]
181 v1_181 [null]
182 v1_182 [null]
183 v1_183 [null]
184 v1_184 [null]
185 v1_185 [null]
186 v1_186 [null]
187 v1_187 [null]
188 v1_188 [null]
189 v1_189 [null]
19 v1_19 [null]
190 v1_190 [null]
191 v1_191 [null]
192 v1_192 [null]
193 v1_193 [null]
194 v1_194 [null]
195 v1_195 [null]
196 v1_196 [null]
197 v1_197 [null]
198 v1_198 [null]
199 v1_199 [null]
2 v1_2 [null]
20 v1_20 [null]
200 v1_200 [null]
201 v1_201 [null]
202 v1_202 [null]
203 v1_203 [null]
204 v1_204 [null]
205 v1_205 [null]
206 v1_206 [null]
207 v1_207 [null]
208 v1_208 [null]
209 v1_209 [null]
21 v1_21 [null]
210 v1_210 [null]
211 v1_211 [null]
212 v1_212 [null]
213 v1_213 [null]
214 v1_214 [null]
215 v1_215 [null]
216 v1_216 [null]
217 v1_217 [null]
218 v1_218 [null]
219 v1_219 [null]
22 v1_22 [null]
220 v1_220 [null]
221 v1_221 [null]
222 v1_222 [null]
223 v1_223 [null]
224 v1_224 [null]
225 v1_225 [null]
226 v1_226 [null]
227 v1_227 [null]
228 v1_228 [null]
229 v1_229 [null]
23 v1_23 [null]
230 v1_230 [null]
231 v1_231 [null]
232 v1_232 [null]
233 v1_233 [null]
234 v1_234 [null]
235 v1_235 [null]
236 v1_236 [null]
237 v1_237 [null]
238 v1_238 [null]
239 v1_239 [null]
24 v1_24 [null]
240 v1_240 [null]
241 v1_241 [null]
242 v1_242 [null]
243 v1_243 [null]
244 v1_244 [null]
245 v1_245 [null]
246 v1_246 [null]
247 v1_247 [null]
248 v1_248 [null]
249 v1_249 [null]
25 v1_25 [null]
250 v1_250 [null]
251 v1_251 [null]
252 v1_252 [null]
253 v1_253 [null]
254 v1_254 [null]
255 v1_255 [null]
26 v1_26 [null]
27 v1_27 [null]
28 v1_28 [null]
29 v1_29 [null]
3 v1_3 [null]
30 v1_30 [null]
31 v1_31 [null]
32 v1_32 [null]
33 v1_33 [null]
34 v1_34 [null]
35 v1_35 [null]
36 v1_36 [null]
37 v1_37 [null]
38 v1_38 [null]
39 v1_39 [null]
4 v1_4 [null]
40 v1_40 [null]
41 v1_41 [null]
42 v1_42 [null]
43 v1_43 [null]
44 v1_44 [null]
45 v1_45 [null]
46 v1_46 [null]
47 v1_47 [null]
48 v1_48 [null]
49 v1_49 [null]
5 v1_5 [null]
50 v1_50 [null]
51 v1_51 [null]
52 v1_52 [null]
53 v1_53 [null]
54 v1_54 [null]
55 v1_55 [null]
56 v1_56 [null]
57 v1_57 [null]
58 v1_58 [null]
59 v1_59 [null]
6 v1_6 [null]
60 v1_60 [null]
61 v1_61 [null]
62 v1_62 [null]
63 v1_63 [null]
64 v1_64 [null]
65 v1_65 [null]
66 v1_66 [null]
67 v1_67 [null]
68 v1_68 [null]
69 v1_69 [null]
7 v1_7 [null]
70 v1_70 [null]
71 v1_71 [null]
72 v1_72 [null]
73 v1_73 [null]
74 v1_74 [null]
75 v1_75 [null]
76 v1_76 [null]
77 v1_77 [null]
78 v1_78 [null]
79 v1_79 [null]
8 v1_8 [null]
80 v1_80 [null]
81 v1_81 [null]
82 v1_82 [null]
83 v1_83 [null]
84 v1_84 [null]
85 v1_85 [null]
86 v1_86 [null]
87 v1_87 [null]
88 v1_88 [null]
89 v1_89 [null]
9 v1_9 [null]
90 v1_90 [null]
91 v1_91 [null]
92 v1_92 [null]
93 v1_93 [null]
94 v1_94 [null]
95 v1_95 [null]
96 v1_96 [null]
97 v1_97 [null]
98 v1_98 [null]
99 v1_99 [null]
HSUPDATEDUMP_TABLE
0 [null] [null]
1 [null] [null]
10 [null] [null]
100 [null] [null]
101 [null] [null]
102 [null] [null]
103 [null] [null]
104 [null] [null]
105 [null] [null]
106 [null] [null]
107 [null] [null]
108 [null] [null]
109 [null] [null]
11 [null] [null]
110 [null] [null]
111 [null] [null]
112 [null] [null]
113 [null] [null]
114 [null] [null]
115 [null] [null]
116 [null] [null]
117 [null] [null]
118 [null] [null]
119 [null] [null]
12 [null] [null]
120 [null] [null]
121 [null] [null]
122 [null] [null]
123 [null] [null]
124 [null] [null]
125 [null] [null]
126 [null] [null]
127 [null] [null]
128 [null] [null]
129 [null] [null]
13 [null] [null]
130 [null] [null]
131 [null] [null]
132 [null] [null]
133 [null] [null]
134 [null] [null]
135 [null] [null]
136 [null] [null]
137 [null] [null]
138 [null] [null]
139 [null] [null]
14 [null] [null]
140 [null] [null]
141 [null] [null]
142 [null] [null]
143 [null] [null]
144 [null] [null]
145 [null] [null]
146 [null] [null]
147 [null] [null]
148 [null] [null]
149 [null] [null]
15 [null] [null]
150 [null] [null]
151 [null] [null]
152 [null] [null]
153 [null] [null]
154 [null] [null]
155 [null] [null]
156 [null] [null]
157 [null] [null]
158 [null] [null]
159 [null] [null]
16 [null] [null]
160 [null] [null]
161 [null] [null]
162 [null] [null]
163 [null] [null]
164 [null] [null]
165 [null] [null]
166 [null] [null]
167 [null] [null]
168 [null] [null]
169 [null] [null]
17 [null] [null]
170 [null] [null]
171 [null] [null]
172 [null] [null]
173 [null] [null]
174 [null] [null]
175 [null] [null]
176 [null] [null]
177 [null] [null]
178 [null] [null]
179 [null] [null]
18 [null] [null]
180 [null] [null]
181 [null] [null]
182 [null] [null]
183 [null] [null]
184 [null] [null]
185 [null] [null]
186 [null] [null]
187 [null] [null]
188 [null] [null]
189 [null] [null]
19 [null] [null]
190 [null] [null]
191 [null] [null]
192 [null] [null]
193 [null] [null]
194 [null] [null]
195 [null] [null]
196 [null] [null]
197 [null] [null]
198 [null] [null]
199 [null] [null]
2 [null] [null]
20 [null] [null]
200 [null] [null]
201 [null] [null]
202 [null] [null]
203 [null] [null]
204 [null] [null]
205 [null] [null]
206 [null] [null]
207 [null] [null]
208 [null] [null]
209 [null] [null]
21 [null] [null]
210 [null] [null]
211 [null] [null]
212 [null] [null]
213 [null] [null]
214 [null] [null]
215 [null] [null]
216 [null] [null]
217 [null] [null]
218 [null] [null]
219 [null] [null]
22 [null] [null]
220 [null] [null]
221 [null] [null]
222 [null] [null]
223 [null] [null]
224 [null] [null]
225 [null] [null]
226 [null] [null]
227 [null] [null]
228 [null] [null]
229 [null] [null]
23 [null] [null]
230 [null] [null]
231 [null] [null]
232 [null] [null]
233 [null] [null]
234 [null] [null]
235 [null] [null]
236 [null] [null]
237 [null] [null]
238 [null] [null]
239 [null] [null]
24 [null] [null]
240 [null] [null]
241 [null] [null]
242 [null] [null]
243 [null] [null]
244 [null] [null]
245 [null] [null]
246 [null] [null]
247 [null] [null]
248 [null] [null]
249 [null] [null]
25 [null] [null]
250 [null] [null]
251 [null] [null]
252 [null] [null]
253 [null] [null]
254 [null] [null]
255 [null] [null]
26 [null] [null]
27 [null] [null]
28 [null] [null]
29 [null] [null]
3 [null] [null]
30 [null] [null]
31 [null] [null]
32 [null] [null]
33 [null] [null]
34 [null] [null]
35 [null] [null]
36 [null] [null]
37 [null] [null]
38 [null] [null]
39 [null] [null]
4 [null] [null]
40 [null] [null]
41 [null] [null]
42 [null] [null]
43 [null] [null]
44 [null] [null]
45 [null] [null]
46 [null] [null]
47 [null] [null]
48 [null] [null]
49 [null] [null]
5 [null] [null]
50 [null] [null]
51 [null] [null]
52 [null] [null]
53 [null] [null]
54 [null] [null]
55 [null] [null]
56 [null] [null]
57 [null] [null]
58 [null] [null]
59 [null] [null]
6 [null] [null]
60 [null] [null]
61 [null] [null]
62 [null] [null]
63 [null] [null]
64 [null] [null]
65 [null] [null]
66 [null] [null]
67 [null] [null]
68 [null] [null]
69 [null] [null]
7 [null] [null]
70 [null] [null]
71 [null] [null]
72 [null] [null]
73 [null] [null]
74 [null] [null]
75 [null] [null]
76 [null] [null]
77 [null] [null]
78 [null] [null]
79 [null] [null]
8 [null] [null]
80 [null] [null]
81 [null] [null]
82 [null] [null]
83 [null] [null]
84 [null] [null]
85 [null] [null]
86 [null] [null]
87 [null] [null]
88 [null] [null]
89 [null] [null]
9 [null] [null]
90 [null] [null]
91 [null] [null]
92 [null] [null]
93 [null] [null]
94 [null] [null]
95 [null] [null]
96 [null] [null]
97 [null] [null]
98 [null] [null]
99 [null] [null]
HSUPDATEDUMP_TABLE
0 hoge [null]
1 hoge [null]
10 hoge [null]
100 hoge [null]
101 hoge [null]
102 hoge [null]
103 hoge [null]
104 hoge [null]
105 hoge [null]
106 hoge [null]
107 hoge [null]
108 hoge [null]
109 hoge [null]
11 hoge [null]
110 hoge [null]
111 hoge [null]
112 hoge [null]
113 hoge [null]
114 hoge [null]
115 hoge [null]
116 hoge [null]
117 hoge [null]
118 hoge [null]
119 hoge [null]
12 hoge [null]
120 hoge [null]
121 hoge [null]
122 hoge [null]
123 hoge [null]
124 hoge [null]
125 hoge [null]
126 hoge [null]
127 hoge [null]
128 hoge [null]
129 hoge [null]
13 hoge [null]
130 hoge [null]
131 hoge [null]
132 hoge [null]
133 hoge [null]
134 hoge [null]
135 hoge [null]
136 hoge [null]
137 hoge [null]
138 hoge [null]
139 hoge [null]
14 hoge [null]
140 hoge [null]
141 hoge [null]
142 hoge [null]
143 hoge [null]
144 hoge [null]
145 hoge [null]
146 hoge [null]
147 hoge [null]
148 hoge [null]
149 hoge [null]
15 hoge [null]
150 hoge [null]
151 hoge [null]
152 hoge [null]
153 hoge [null]
154 hoge [null]
155 hoge [null]
156 hoge [null]
157 hoge [null]
158 hoge [null]
159 hoge [null]
16 hoge [null]
160 hoge [null]
161 hoge [null]
162 hoge [null]
163 hoge [null]
164 hoge [null]
165 hoge [null]
166 hoge [null]
167 hoge [null]
168 hoge [null]
169 hoge [null]
17 hoge [null]
170 hoge [null]
171 hoge [null]
172 hoge [null]
173 hoge [null]
174 hoge [null]
175 hoge [null]
176 hoge [null]
177 hoge [null]
178 hoge [null]
179 hoge [null]
18 hoge [null]
180 hoge [null]
181 hoge [null]
182 hoge [null]
183 hoge [null]
184 hoge [null]
185 hoge [null]
186 hoge [null]
187 hoge [null]
188 hoge [null]
189 hoge [null]
19 hoge [null]
190 hoge [null]
191 hoge [null]
192 hoge [null]
193 hoge [null]
194 hoge [null]
195 hoge [null]
196 hoge [null]
197 hoge [null]
198 hoge [null]
199 hoge [null]
2 hoge [null]
20 hoge [null]
200 hoge [null]
201 hoge [null]
202 hoge [null]
203 hoge [null]
204 hoge [null]
205 hoge [null]
206 hoge [null]
207 hoge [null]
208 hoge [null]
209 hoge [null]
21 hoge [null]
210 hoge [null]
211 hoge [null]
212 hoge [null]
213 hoge [null]
214 hoge [null]
215 hoge [null]
216 hoge [null]
217 hoge [null]
218 hoge [null]
219 hoge [null]
22 hoge [null]
220 hoge [null]
221 hoge [null]
222 hoge [null]
223 hoge [null]
224 hoge [null]
225 hoge [null]
226 hoge [null]
227 hoge [null]
228 hoge [null]
229 hoge [null]
23 hoge [null]
230 hoge [null]
231 hoge [null]
232 hoge [null]
233 hoge [null]
234 hoge [null]
235 hoge [null]
236 hoge [null]
237 hoge [null]
238 hoge [null]
239 hoge [null]
24 hoge [null]
240 hoge [null]
241 hoge [null]
242 hoge [null]
243 hoge [null]
244 hoge [null]
245 hoge [null]
246 hoge [null]
247 hoge [null]
248 hoge [null]
249 hoge [null]
25 hoge [null]
250 hoge [null]
251 hoge [null]
252 hoge [null]
253 hoge [null]
254 hoge [null]
255 hoge [null]
26 hoge [null]
27 hoge [null]
28 hoge [null]
29 hoge [null]
3 hoge [null]
30 hoge [null]
31 hoge [null]
32 hoge [null]
33 hoge [null]
34 hoge [null]
35 hoge [null]
36 hoge [null]
37 hoge [null]
38 hoge [null]
39 hoge [null]
4 hoge [null]
40 hoge [null]
41 hoge [null]
42 hoge [null]
43 hoge [null]
44 hoge [null]
45 hoge [null]
46 hoge [null]
47 hoge [null]
48 hoge [null]
49 hoge [null]
5 hoge [null]
50 hoge [null]
51 hoge [null]
52 hoge [null]
53 hoge [null]
54 hoge [null]
55 hoge [null]
56 hoge [null]
57 hoge [null]
58 hoge [null]
59 hoge [null]
6 hoge [null]
60 hoge [null]
61 hoge [null]
62 hoge [null]
63 hoge [null]
64 hoge [null]
65 hoge [null]
66 hoge [null]
67 hoge [null]
68 hoge [null]
69 hoge [null]
7 hoge [null]
70 hoge [null]
71 hoge [null]
72 hoge [null]
73 hoge [null]
74 hoge [null]
75 hoge [null]
76 hoge [null]
77 hoge [null]
78 hoge [null]
79 hoge [null]
8 hoge [null]
80 hoge [null]
81 hoge [null]
82 hoge [null]
83 hoge [null]
84 hoge [null]
85 hoge [null]
86 hoge [null]
87 hoge [null]
88 hoge [null]
89 hoge [null]
9 hoge [null]
90 hoge [null]
91 hoge [null]
92 hoge [null]
93 hoge [null]
94 hoge [null]
95 hoge [null]
96 hoge [null]
97 hoge [null]
98 hoge [null]
99 hoge [null]
#!/usr/bin/perl
# vim:sw=2:ai
# test for nulls
BEGIN {
push @INC, "../common/";
};
use strict;
use warnings;
use hstest;
my $dbh = hstest::init_testdb();
my $table = 'hstesttbl';
my $tablesize = 256;
$dbh->do(
"create table $table (" .
"k varchar(30) primary key, " .
"v1 varchar(30), " .
"v2 varchar(30)) " .
"engine = innodb default charset = binary");
srand(999);
my %valmap = ();
# setting null
print "HSINSERT";
my $hs = hstest::get_hs_connection(undef, 9999);
my $dbname = $hstest::conf{dbname};
$hs->open_index(1, $dbname, $table, '', 'k,v1,v2');
for (my $i = 0; $i < $tablesize; ++$i) {
my $k = "" . $i;
my $v1 = "v1_" . $i;
my $v2 = undef; # null value
my $r = $hs->execute_insert(1, [ $k, $v1, $v2 ]);
my $err = $r->[0];
if ($err != 0) {
my $err_str = $r->[1];
print "$err $err_str\n";
}
}
undef $hs;
dump_table();
# setting null
print "HSUPDATE";
$hs = hstest::get_hs_connection(undef, 9999);
$dbname = $hstest::conf{dbname};
$hs->open_index(2, $dbname, $table, '', 'v1');
for (my $i = 0; $i < $tablesize; ++$i) {
my $r = $hs->execute_single(2, '=', [ $i ], 1000, 0, 'U', [ undef ]);
my $err = $r->[0];
if ($err != 0) {
my $err_str = $r->[1];
print "$err $err_str\n";
}
}
undef $hs;
dump_table();
# setting non-null
print "HSUPDATE";
$hs = hstest::get_hs_connection(undef, 9999);
$dbname = $hstest::conf{dbname};
$hs->open_index(2, $dbname, $table, '', 'v1');
for (my $i = 0; $i < $tablesize; ++$i) {
my $r = $hs->execute_single(2, '=', [ $i ], 1000, 0, 'U', [ "hoge" ]);
my $err = $r->[0];
if ($err != 0) {
my $err_str = $r->[1];
print "$err $err_str\n";
}
}
undef $hs;
dump_table();
sub dump_table {
print "DUMP_TABLE\n";
my $aref = $dbh->selectall_arrayref("select k,v1,v2 from $table order by k");
for my $row (@$aref) {
my ($k, $v1, $v2) = @$row;
$v1 = "[null]" if !defined($v1);
$v2 = "[null]" if !defined($v2);
print "$k $v1 $v2\n";
# print "MISMATCH\n" if ($valmap{$k} ne $v);
}
}
VAL
[0][k5][5]
[0][k6][6]
[0][k7][7]
[0][k8][8]
INCREMENT
[0][1]
[0][1]
[0][1]
[0][1]
VAL
[0][k5][8]
[0][k6][18]
[0][k7][-4]
[0][k8][-7]
DECREMENT
[0][1]
[0][0]
[0][1]
[0][0]
VAL
[0][k5][6]
[0][k6][18]
[0][k7][-84]
[0][k8][-7]
INCREMENT
[0][6]
[0][7]
[0][8]
[0][9]
[0][10]
[0][11]
[0][12]
[0][13]
[0][14]
VAL
[0][k5][15]
#!/usr/bin/perl
# vim:sw=2:ai
# test for increment/decrement
BEGIN {
push @INC, "../common/";
};
use strict;
use warnings;
use hstest;
my $dbh = hstest::init_testdb();
my $table = 'hstesttbl';
my $tablesize = 100;
$dbh->do(
"create table $table (k varchar(30) primary key, v varchar(30) not null) " .
"engine = innodb");
srand(999);
my %valmap = ();
my $sth = $dbh->prepare("insert into $table values (?,?)");
for (my $i = 0; $i < $tablesize; ++$i) {
my $k = "k" . $i;
my $v = $i;
$sth->execute($k, $v);
$valmap{$k} = $v;
}
my $hs = hstest::get_hs_connection(undef, 9999);
my $dbname = $hstest::conf{dbname};
$hs->open_index(1, $dbname, $table, '', 'k,v');
$hs->open_index(2, $dbname, $table, '', 'v');
exec_multi(
"VAL",
[ 1, '=', [ 'k5' ], 1, 0 ],
[ 1, '=', [ 'k6' ], 1, 0 ],
[ 1, '=', [ 'k7' ], 1, 0 ],
[ 1, '=', [ 'k8' ], 1, 0 ],
);
# 5, 6, 7, 8
exec_multi(
"INCREMENT",
[ 2, '=', [ 'k5' ], 1, 0, '+', [ 3 ] ],
[ 2, '=', [ 'k6' ], 1, 0, '+', [ 12 ] ],
[ 2, '=', [ 'k7' ], 1, 0, '+', [ -11 ] ],
[ 2, '=', [ 'k8' ], 1, 0, '+', [ -15 ] ],
);
exec_multi(
"VAL",
[ 1, '=', [ 'k5' ], 1, 0 ],
[ 1, '=', [ 'k6' ], 1, 0 ],
[ 1, '=', [ 'k7' ], 1, 0 ],
[ 1, '=', [ 'k8' ], 1, 0 ],
);
# 8, 18, -4, -7
exec_multi(
"DECREMENT",
[ 2, '=', [ 'k5' ], 1, 0, '-', [ 2 ] ],
[ 2, '=', [ 'k6' ], 1, 0, '-', [ 24 ] ],
[ 2, '=', [ 'k7' ], 1, 0, '-', [ 80 ] ],
[ 2, '=', [ 'k8' ], 1, 0, '-', [ -80 ] ],
);
# mod, no, mod, no
exec_multi(
"VAL",
[ 1, '=', [ 'k5' ], 1, 0 ],
[ 1, '=', [ 'k6' ], 1, 0 ],
[ 1, '=', [ 'k7' ], 1, 0 ],
[ 1, '=', [ 'k8' ], 1, 0 ],
);
# 6, 18, -84, -7
exec_multi(
"INCREMENT",
[ 2, '=', [ 'k5' ], 1, 0, '+?', [ 1 ] ],
[ 2, '=', [ 'k5' ], 1, 0, '+?', [ 1 ] ],
[ 2, '=', [ 'k5' ], 1, 0, '+?', [ 1 ] ],
[ 2, '=', [ 'k5' ], 1, 0, '+?', [ 1 ] ],
[ 2, '=', [ 'k5' ], 1, 0, '+?', [ 1 ] ],
[ 2, '=', [ 'k5' ], 1, 0, '+?', [ 1 ] ],
[ 2, '=', [ 'k5' ], 1, 0, '+?', [ 1 ] ],
[ 2, '=', [ 'k5' ], 1, 0, '+?', [ 1 ] ],
[ 2, '=', [ 'k5' ], 1, 0, '+?', [ 1 ] ],
);
exec_multi(
"VAL",
[ 1, '=', [ 'k5' ], 1, 0 ],
);
# 15
sub exec_multi {
my $mess = shift(@_);
print "$mess\n";
my $mres = $hs->execute_multi(\@_);
for my $res (@$mres) {
for my $fld (@$res) {
print "[$fld]";
}
print "\n";
}
}
VAL
code=0
[k1_0][k2_0][0][0]
[k1_0][k2_1][1][0]
[k1_0][k2_2][2][0]
[k1_0][k2_3][3][0]
[k1_0][k2_4][4][0]
[k1_0][k2_5][5][0]
[k1_0][k2_6][6][0]
[k1_0][k2_7][7][0]
[k1_0][k2_8][8][0]
[k1_0][k2_9][9][0]
[k1_1][k2_0][1][0]
[k1_1][k2_1][2][1]
[k1_1][k2_2][3][2]
[k1_1][k2_3][4][3]
[k1_1][k2_4][5][4]
[k1_1][k2_5][6][5]
[k1_1][k2_6][7][6]
[k1_1][k2_7][8][7]
[k1_1][k2_8][9][8]
[k1_1][k2_9][10][9]
[k1_2][k2_0][2][0]
[k1_2][k2_1][3][2]
[k1_2][k2_2][4][4]
[k1_2][k2_3][5][6]
[k1_2][k2_4][6][8]
[k1_2][k2_5][7][10]
[k1_2][k2_6][8][12]
[k1_2][k2_7][9][14]
[k1_2][k2_8][10][16]
[k1_2][k2_9][11][18]
[k1_3][k2_0][3][0]
[k1_3][k2_1][4][3]
[k1_3][k2_2][5][6]
[k1_3][k2_3][6][9]
[k1_3][k2_4][7][12]
[k1_3][k2_5][8][15]
[k1_3][k2_6][9][18]
[k1_3][k2_7][10][21]
[k1_3][k2_8][11][24]
[k1_3][k2_9][12][27]
[k1_4][k2_0][4][0]
[k1_4][k2_1][5][4]
[k1_4][k2_2][6][8]
[k1_4][k2_3][7][12]
[k1_4][k2_4][8][16]
[k1_4][k2_5][9][20]
[k1_4][k2_6][10][24]
[k1_4][k2_7][11][28]
[k1_4][k2_8][12][32]
[k1_4][k2_9][13][36]
[k1_5][k2_0][5][0]
[k1_5][k2_1][6][5]
[k1_5][k2_2][7][10]
[k1_5][k2_3][8][15]
[k1_5][k2_4][9][20]
[k1_5][k2_5][10][25]
[k1_5][k2_6][11][30]
[k1_5][k2_7][12][35]
[k1_5][k2_8][13][40]
[k1_5][k2_9][14][45]
[k1_6][k2_0][6][0]
[k1_6][k2_1][7][6]
[k1_6][k2_2][8][12]
[k1_6][k2_3][9][18]
[k1_6][k2_4][10][24]
[k1_6][k2_5][11][30]
[k1_6][k2_6][12][36]
[k1_6][k2_7][13][42]
[k1_6][k2_8][14][48]
[k1_6][k2_9][15][54]
[k1_7][k2_0][7][0]
[k1_7][k2_1][8][7]
[k1_7][k2_2][9][14]
[k1_7][k2_3][10][21]
[k1_7][k2_4][11][28]
[k1_7][k2_5][12][35]
[k1_7][k2_6][13][42]
[k1_7][k2_7][14][49]
[k1_7][k2_8][15][56]
[k1_7][k2_9][16][63]
[k1_8][k2_0][8][0]
[k1_8][k2_1][9][8]
[k1_8][k2_2][10][16]
[k1_8][k2_3][11][24]
[k1_8][k2_4][12][32]
[k1_8][k2_5][13][40]
[k1_8][k2_6][14][48]
[k1_8][k2_7][15][56]
[k1_8][k2_8][16][64]
[k1_8][k2_9][17][72]
[k1_9][k2_0][9][0]
[k1_9][k2_1][10][9]
[k1_9][k2_2][11][18]
[k1_9][k2_3][12][27]
[k1_9][k2_4][13][36]
[k1_9][k2_5][14][45]
[k1_9][k2_6][15][54]
[k1_9][k2_7][16][63]
[k1_9][k2_8][17][72]
[k1_9][k2_9][18][81]
FILTER
code=0
[k1_0][k2_5][5][0]
[k1_1][k2_5][6][5]
[k1_2][k2_5][7][10]
[k1_3][k2_5][8][15]
[k1_4][k2_5][9][20]
[k1_5][k2_5][10][25]
[k1_6][k2_5][11][30]
[k1_7][k2_5][12][35]
[k1_8][k2_5][13][40]
[k1_9][k2_5][14][45]
FILTER
code=0
[k1_0][k2_5][5][0]
[k1_1][k2_5][6][5]
[k1_2][k2_5][7][10]
[k1_3][k2_5][8][15]
[k1_4][k2_5][9][20]
[k1_5][k2_5][10][25]
[k1_6][k2_5][11][30]
[k1_7][k2_5][12][35]
[k1_8][k2_5][13][40]
[k1_9][k2_5][14][45]
FILTER
code=0
[k1_0][k2_3][3][0]
[k1_1][k2_2][3][2]
[k1_2][k2_1][3][2]
[k1_3][k2_0][3][0]
FILTER
code=0
[k1_1][k2_0][1][0]
[k1_1][k2_1][2][1]
[k1_1][k2_2][3][2]
[k1_1][k2_3][4][3]
[k1_1][k2_4][5][4]
[k1_1][k2_5][6][5]
[k1_1][k2_6][7][6]
[k1_1][k2_7][8][7]
[k1_1][k2_8][9][8]
[k1_1][k2_9][10][9]
[k1_2][k2_0][2][0]
[k1_2][k2_1][3][2]
[k1_2][k2_2][4][4]
[k1_2][k2_3][5][6]
[k1_2][k2_4][6][8]
[k1_2][k2_5][7][10]
[k1_2][k2_6][8][12]
[k1_2][k2_7][9][14]
[k1_2][k2_8][10][16]
[k1_2][k2_9][11][18]
FILTER
code=0
[k1_2][k2_5][7][10]
[k1_2][k2_6][8][12]
[k1_2][k2_7][9][14]
[k1_2][k2_8][10][16]
[k1_2][k2_9][11][18]
FILTER
code=0
[2]
VAL
code=0
[k1_0][k2_0][0][0]
[k1_0][k2_1][1][0]
[k1_0][k2_2][2][0]
[k1_0][k2_3][3][0]
[k1_0][k2_4][4][0]
[k1_0][k2_5][5][0]
[k1_0][k2_6][6][0]
[k1_0][k2_7][7][0]
[k1_0][k2_8][8][0]
[k1_0][k2_9][9][0]
[k1_1][k2_0][1][0]
[k1_1][k2_1][2][1]
[k1_1][k2_2][3][2]
[k1_1][k2_3][4][3]
[k1_1][k2_4][5][4]
[k1_1][k2_5][6][5]
[k1_1][k2_6][7][6]
[k1_1][k2_7][8][7]
[k1_1][k2_8][9][8]
[k1_1][k2_9][10][9]
[k1_2][k2_0][2][0]
[k1_2][k2_1][3][2]
[k1_2][k2_2][4][4]
[k1_2][k2_3][5][6]
[k1_2][k2_4][6][8]
[k1_2][k2_5][-1][10]
[k1_2][k2_6][8][12]
[k1_2][k2_7][9][14]
[k1_2][k2_8][10][16]
[k1_2][k2_9][11][18]
[k1_3][k2_0][3][0]
[k1_3][k2_1][4][3]
[k1_3][k2_2][5][6]
[k1_3][k2_3][6][9]
[k1_3][k2_4][7][12]
[k1_3][k2_5][8][15]
[k1_3][k2_6][9][18]
[k1_3][k2_7][10][21]
[k1_3][k2_8][11][24]
[k1_3][k2_9][12][27]
[k1_4][k2_0][4][0]
[k1_4][k2_1][5][4]
[k1_4][k2_2][6][8]
[k1_4][k2_3][7][12]
[k1_4][k2_4][8][16]
[k1_4][k2_5][9][20]
[k1_4][k2_6][10][24]
[k1_4][k2_7][11][28]
[k1_4][k2_8][12][32]
[k1_4][k2_9][13][36]
[k1_5][k2_0][5][0]
[k1_5][k2_1][6][5]
[k1_5][k2_2][-1][10]
[k1_5][k2_3][8][15]
[k1_5][k2_4][9][20]
[k1_5][k2_5][10][25]
[k1_5][k2_6][11][30]
[k1_5][k2_7][12][35]
[k1_5][k2_8][13][40]
[k1_5][k2_9][14][45]
[k1_6][k2_0][6][0]
[k1_6][k2_1][7][6]
[k1_6][k2_2][8][12]
[k1_6][k2_3][9][18]
[k1_6][k2_4][10][24]
[k1_6][k2_5][11][30]
[k1_6][k2_6][12][36]
[k1_6][k2_7][13][42]
[k1_6][k2_8][14][48]
[k1_6][k2_9][15][54]
[k1_7][k2_0][7][0]
[k1_7][k2_1][8][7]
[k1_7][k2_2][9][14]
[k1_7][k2_3][10][21]
[k1_7][k2_4][11][28]
[k1_7][k2_5][12][35]
[k1_7][k2_6][13][42]
[k1_7][k2_7][14][49]
[k1_7][k2_8][15][56]
[k1_7][k2_9][16][63]
[k1_8][k2_0][8][0]
[k1_8][k2_1][9][8]
[k1_8][k2_2][10][16]
[k1_8][k2_3][11][24]
[k1_8][k2_4][12][32]
[k1_8][k2_5][13][40]
[k1_8][k2_6][14][48]
[k1_8][k2_7][15][56]
[k1_8][k2_8][16][64]
[k1_8][k2_9][17][72]
[k1_9][k2_0][9][0]
[k1_9][k2_1][10][9]
[k1_9][k2_2][11][18]
[k1_9][k2_3][12][27]
[k1_9][k2_4][13][36]
[k1_9][k2_5][14][45]
[k1_9][k2_6][15][54]
[k1_9][k2_7][16][63]
[k1_9][k2_8][17][72]
[k1_9][k2_9][18][81]
#!/usr/bin/perl
# vim:sw=2:ai
# test for filters
BEGIN {
push @INC, "../common/";
};
use strict;
use warnings;
use hstest;
my $dbh = hstest::init_testdb();
my $table = 'hstesttbl';
my $tablesize = 10;
$dbh->do(
"create table $table " .
"(k1 varchar(30) not null, k2 varchar(30) not null, " .
"v1 int not null, v2 int not null, " .
"primary key (k1, k2) ) engine = innodb");
srand(999);
my $sth = $dbh->prepare("insert into $table values (?,?,?,?)");
for (my $i = 0; $i < $tablesize; ++$i) {
for (my $j = 0; $j < $tablesize; ++$j) {
my $k1 = "k1_" . $i;
my $k2 = "k2_" . $j;
my $v1 = $i + $j;
my $v2 = $i * $j;
$sth->execute($k1, $k2, $v1, $v2);
}
}
my $hs = hstest::get_hs_connection(undef, 9999);
my $dbname = $hstest::conf{dbname};
$hs->open_index(1, $dbname, $table, '', 'k1,k2,v1,v2', 'k2');
$hs->open_index(2, $dbname, $table, '', 'k1,k2,v1,v2', 'k1,k2,v1,v2');
$hs->open_index(3, $dbname, $table, '', 'v1', 'k1,v2');
exec_multi(
4, "VAL",
[ 1, '>=', [ '', '' ], 1000, 0 ],
);
# all
# select k1, k2, v1, v2 ... where (k1, k2) >= ('', '') and k2 = 'k2_5'
exec_single(
4, "FILTER",
[ 1, '>=', [ '', '' ], 1000, 0, undef, undef, [ [ 'F', '=', 0, 'k2_5' ] ] ]
);
# same as above
exec_multi(
4, "FILTER",
[ 1, '>=', [ '', '' ], 1000, 0, undef, undef, [ [ 'F', '=', 0, 'k2_5' ] ] ],
);
# select k1, k2, v1, v2 ... where (k1, k2) >= ('', '') and v1 = 3
exec_multi(
4, "FILTER",
[ 2, '>=', [ '', '' ], 1000, 0, undef, undef, [ [ 'F', '=', 2, 3 ] ] ],
);
# select k1, k2, v1, v2 ... where (k1, k2) >= ('k1_1', '') and k1 <= 'k1_2'
exec_multi(
4, "FILTER",
[ 2, '>=', [ 'k1_1', '' ], 1000, 0, undef, undef,
[ [ 'W', '<=', 0, 'k1_2' ] ] ],
);
# select k1, k2, v1, v2 ... where (k1, k2) >= ('k1_1', '') and k1 <= 'k1_2'
# and v2 >= 10
exec_multi(
4, "FILTER",
[ 2, '>=', [ 'k1_1', '' ], 1000, 0, undef, undef,
[ [ 'W', '<=', 0, 'k1_2' ], [ 'F', '>=', 3, 10 ] ] ],
);
# update ... set v2 = -1 where (k1, k2) >= ('k1_3', '') and v2 = 10
exec_multi(
4, "FILTER",
[ 3, '>=', [ 'k1_1', '' ], 1000, 0, 'U', [ -1 ],
[ [ 'F', '=', 1, 10 ] ] ],
);
exec_multi(
4, "VAL",
[ 1, '>=', [ '', '' ], 1000, 0 ],
);
# all
exit 0;
sub exec_single {
my ($width, $mess, $req) = @_;
print "$mess\n";
my $res = $hs->execute_single(@$req);
{
my $code = shift(@$res);
print "code=$code\n";
my $i = 0;
for my $fld (@$res) {
print "[$fld]";
if (++$i >= $width) {
print "\n";
$i = 0;
}
}
print "\n";
}
}
sub exec_multi {
my $width = shift(@_);
my $mess = shift(@_);
print "$mess\n";
my $mres = $hs->execute_multi(\@_);
for my $res (@$mres) {
my $code = shift(@$res);
print "code=$code\n";
my $i = 0;
for my $fld (@$res) {
print "[$fld]";
if (++$i >= $width) {
print "\n";
$i = 0;
}
}
print "\n";
}
}
HSINSERT
DUMP_TABLE
1 v1sql_0 v2sql_0
2 v1sql_1 v2sql_1
3 v1sql_2 v2sql_2
4 v1sql_3 v2sql_3
5 v1sql_4 v2sql_4
6 v1sql_5 v2sql_5
7 v1sql_6 v2sql_6
8 v1sql_7 v2sql_7
9 v1sql_8 v2sql_8
10 v1sql_9 v2sql_9
11 v1hs_0 v2hs_0
12 v1hs_1 v2hs_1
13 v1hs_2 v2hs_2
14 v1hs_3 v2hs_3
15 v1hs_4 v2hs_4
16 v1hs_5 v2hs_5
17 v1hs_6 v2hs_6
18 v1hs_7 v2hs_7
19 v1hs_8 v2hs_8
20 v1hs_9 v2hs_9
21 v1hs3_0 v2hs3_0
22 v1hs3_0 v2hs3_0
23 v1hs3_0 v2hs3_0
24 v1hs3_1 v2hs3_1
25 v1hs3_1 v2hs3_1
26 v1hs3_1 v2hs3_1
27 v1hs3_2 v2hs3_2
28 v1hs3_2 v2hs3_2
29 v1hs3_2 v2hs3_2
30 v1hs3_3 v2hs3_3
31 v1hs3_3 v2hs3_3
32 v1hs3_3 v2hs3_3
33 v1hs3_4 v2hs3_4
34 v1hs3_4 v2hs3_4
35 v1hs3_4 v2hs3_4
36 v1hs3_5 v2hs3_5
37 v1hs3_5 v2hs3_5
38 v1hs3_5 v2hs3_5
39 v1hs3_6 v2hs3_6
40 v1hs3_6 v2hs3_6
41 v1hs3_6 v2hs3_6
42 v1hs3_7 v2hs3_7
43 v1hs3_7 v2hs3_7
44 v1hs3_7 v2hs3_7
45 v1hs3_8 v2hs3_8
46 v1hs3_8 v2hs3_8
47 v1hs3_8 v2hs3_8
48 v1hs3_9 v2hs3_9
49 v1hs3_9 v2hs3_9
50 v1hs3_9 v2hs3_9
#!/usr/bin/perl
# vim:sw=2:ai
# test for auto_increment
BEGIN {
push @INC, "../common/";
};
use strict;
use warnings;
use hstest;
my $dbh = hstest::init_testdb();
my $table = 'hstesttbl';
my $tablesize = 10;
$dbh->do(
"create table $table (" .
"k int primary key auto_increment, " .
"v1 varchar(30), " .
"v2 varchar(30)) " .
"engine = myisam default charset = binary");
srand(999);
my $sth = $dbh->prepare("insert into $table values (?,?,?)");
for (my $i = 0; $i < $tablesize; ++$i) {
my $k = 0;
my $v1 = "v1sql_" . $i;
my $v2 = "v2sql_" . $i;
$sth->execute($k, $v1, $v2);
}
my %valmap = ();
print "HSINSERT\n";
my $hs = hstest::get_hs_connection(undef, 9999);
my $dbname = $hstest::conf{dbname};
$hs->open_index(1, $dbname, $table, '', 'k,v1,v2');
# inserts with auto_increment
for (my $i = 0; $i < $tablesize; ++$i) {
my $k = 0;
my $v1 = "v1hs_" . $i;
my $v2 = "v2hs_" . $i;
my $r = $hs->execute_insert(1, [ $k, $v1, $v2 ]);
my $err = $r->[0];
if ($err != 0) {
my $err_str = $r->[1];
print "$err $err_str\n";
}
}
# make sure that it works even when inserts are pipelined. these requests
# are possibly executed in a single transaction.
for (my $i = 0; $i < $tablesize; ++$i) {
my $k = 0;
my $v1 = "v1hs3_" . $i;
my $v2 = "v2hs3_" . $i;
my $r = $hs->execute_multi([
[ 1, '+', [$k, $v1, $v2] ],
[ 1, '+', [$k, $v1, $v2] ],
[ 1, '+', [$k, $v1, $v2] ],
]);
for (my $i = 0; $i < 3; ++$i) {
my $err = $r->[$i]->[0];
if ($err != 0) {
my $err_str = $r->[1];
print "$err $err_str\n";
}
}
}
undef $hs;
dump_table();
sub dump_table {
print "DUMP_TABLE\n";
my $aref = $dbh->selectall_arrayref("select k,v1,v2 from $table order by k");
for my $row (@$aref) {
my ($k, $v1, $v2) = @$row;
$v1 = "[null]" if !defined($v1);
$v2 = "[null]" if !defined($v2);
print "$k $v1 $v2\n";
# print "MISMATCH\n" if ($valmap{$k} ne $v);
}
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment