summaryrefslogtreecommitdiffstats
path: root/storage/spider/hs_client
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 18:07:14 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 18:07:14 +0000
commita175314c3e5827eb193872241446f2f8f5c9d33c (patch)
treecd3d60ca99ae00829c52a6ca79150a5b6e62528b /storage/spider/hs_client
parentInitial commit. (diff)
downloadmariadb-10.5-a175314c3e5827eb193872241446f2f8f5c9d33c.tar.xz
mariadb-10.5-a175314c3e5827eb193872241446f2f8f5c9d33c.zip
Adding upstream version 1:10.5.12.upstream/1%10.5.12upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'storage/spider/hs_client')
-rw-r--r--storage/spider/hs_client/allocator.hpp43
-rw-r--r--storage/spider/hs_client/auto_addrinfo.hpp49
-rw-r--r--storage/spider/hs_client/auto_file.hpp67
-rw-r--r--storage/spider/hs_client/auto_ptrcontainer.hpp70
-rw-r--r--storage/spider/hs_client/config.cpp292
-rw-r--r--storage/spider/hs_client/config.hpp81
-rw-r--r--storage/spider/hs_client/escape.cpp129
-rw-r--r--storage/spider/hs_client/escape.hpp64
-rw-r--r--storage/spider/hs_client/fatal.cpp49
-rw-r--r--storage/spider/hs_client/fatal.hpp31
-rw-r--r--storage/spider/hs_client/hs_compat.h41
-rw-r--r--storage/spider/hs_client/hstcpcli.cpp667
-rw-r--r--storage/spider/hs_client/hstcpcli.hpp98
-rw-r--r--storage/spider/hs_client/mutex.hpp48
-rw-r--r--storage/spider/hs_client/socket.cpp313
-rw-r--r--storage/spider/hs_client/socket.hpp62
-rw-r--r--storage/spider/hs_client/string_buffer.hpp146
-rw-r--r--storage/spider/hs_client/string_ref.hpp106
-rw-r--r--storage/spider/hs_client/string_util.cpp207
-rw-r--r--storage/spider/hs_client/string_util.hpp51
-rw-r--r--storage/spider/hs_client/thread.hpp84
-rw-r--r--storage/spider/hs_client/util.hpp25
22 files changed, 2723 insertions, 0 deletions
diff --git a/storage/spider/hs_client/allocator.hpp b/storage/spider/hs_client/allocator.hpp
new file mode 100644
index 00000000..c302e078
--- /dev/null
+++ b/storage/spider/hs_client/allocator.hpp
@@ -0,0 +1,43 @@
+
+// vim:sw=2:ai
+
+/*
+ * Copyright (C) 2010-2011 DeNA Co.,Ltd.. All rights reserved.
+ * Copyright (C) 2011-2017 Kentoku SHIBA
+ * See COPYRIGHT.txt for details.
+ */
+
+#ifndef DENA_ALLOCATOR_HPP
+#define DENA_ALLOCATOR_HPP
+
+#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)
+#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)
+#endif
+
+#if 1
+#define DENA_ALLOCA_ALLOCATE(typ, len) \
+ (typ *) (alloca((len) * sizeof(typ)))
+#define DENA_ALLOCA_FREE(x)
+#else
+#define DENA_ALLOCA_ALLOCATE(typ, len) \
+ (typ *) (malloc((len) * sizeof(typ)))
+#define DENA_ALLOCA_FREE(x) free(x)
+#endif
+
+#endif
+
diff --git a/storage/spider/hs_client/auto_addrinfo.hpp b/storage/spider/hs_client/auto_addrinfo.hpp
new file mode 100644
index 00000000..5262ad11
--- /dev/null
+++ b/storage/spider/hs_client/auto_addrinfo.hpp
@@ -0,0 +1,49 @@
+
+// vim:sw=2:ai
+
+/*
+ * Copyright (C) 2010-2011 DeNA Co.,Ltd.. All rights reserved.
+ * See COPYRIGHT.txt for details.
+ */
+
+#ifndef DENA_AUTO_ADDRINFO_HPP
+#define DENA_AUTO_ADDRINFO_HPP
+
+#ifndef __WIN__
+#include <netdb.h>
+#endif
+
+#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;
+ hints.ai_flags = flags;
+ hints.ai_family = family;
+ hints.ai_socktype = socktype;
+ hints.ai_protocol = protocol;
+ return getaddrinfo(node, service, &hints, &addr);
+ }
+ private:
+ addrinfo *addr;
+};
+
+};
+
+#endif
+
diff --git a/storage/spider/hs_client/auto_file.hpp b/storage/spider/hs_client/auto_file.hpp
new file mode 100644
index 00000000..ddd1f8c9
--- /dev/null
+++ b/storage/spider/hs_client/auto_file.hpp
@@ -0,0 +1,67 @@
+
+// vim:sw=2:ai
+
+/*
+ * Copyright (C) 2010-2011 DeNA Co.,Ltd.. All rights reserved.
+ * See COPYRIGHT.txt for details.
+ */
+
+#ifndef DENA_AUTO_FILE_HPP
+#define DENA_AUTO_FILE_HPP
+
+/*
+#ifndef __WIN__
+#include <dirent.h>
+#endif
+*/
+
+#include "util.hpp"
+
+namespace dena {
+
+struct auto_file : private noncopyable {
+ auto_file() : fd(-1) { }
+ ~auto_file() {
+ reset();
+ }
+ int get() const { return fd; }
+ int close() {
+ if (fd < 0) {
+ return 0;
+ }
+ const int r = ::close(fd);
+ fd = -1;
+ return r;
+ }
+ void reset(int x = -1) {
+ if (fd >= 0) {
+ this->close();
+ }
+ fd = x;
+ }
+ private:
+ int fd;
+};
+
+/*
+struct auto_dir : private noncopyable {
+ auto_dir() : dp(0) { }
+ ~auto_dir() {
+ reset();
+ }
+ DIR *get() const { return dp; }
+ void reset(DIR *d = 0) {
+ if (dp != 0) {
+ closedir(dp);
+ }
+ dp = d;
+ }
+ private:
+ DIR *dp;
+};
+*/
+
+};
+
+#endif
+
diff --git a/storage/spider/hs_client/auto_ptrcontainer.hpp b/storage/spider/hs_client/auto_ptrcontainer.hpp
new file mode 100644
index 00000000..3629f19c
--- /dev/null
+++ b/storage/spider/hs_client/auto_ptrcontainer.hpp
@@ -0,0 +1,70 @@
+
+// vim:sw=2:ai
+
+/*
+ * Copyright (C) 2010-2011 DeNA Co.,Ltd.. All rights reserved.
+ * Copyright (C) 2011 Kentoku SHIBA
+ * See COPYRIGHT.txt for details.
+ */
+
+#ifndef DENA_AUTO_PTRCONTAINER_HPP
+#define DENA_AUTO_PTRCONTAINER_HPP
+
+namespace dena {
+
+/*
+template <typename Tcnt>
+struct auto_ptrcontainer {
+ typedef Tcnt container_type;
+ typedef typename container_type::value_type value_type;
+ typedef typename container_type::pointer pointer;
+ typedef typename container_type::reference reference;
+ typedef typename container_type::const_reference const_reference;
+ typedef typename container_type::size_type size_type;
+ typedef typename container_type::difference_type difference_type;
+ typedef typename container_type::iterator iterator;
+ typedef typename container_type::const_iterator const_iterator;
+ typedef typename container_type::reverse_iterator reverse_iterator;
+ typedef typename container_type::const_reverse_iterator
+ const_reverse_iterator;
+ iterator begin() { return cnt.begin(); }
+ const_iterator begin() const { return cnt.begin(); }
+ iterator end() { return cnt.end(); }
+ const_iterator end() const { return cnt.end(); }
+ reverse_iterator rbegin() { return cnt.rbegin(); }
+ reverse_iterator rend() { return cnt.rend(); }
+ const_reverse_iterator rbegin() const { return cnt.rbegin(); }
+ const_reverse_iterator rend() const { return cnt.rend(); }
+ size_type size() const { return cnt.size(); }
+ size_type max_size() const { return cnt.max_size(); }
+ bool empty() const { return cnt.empty(); }
+ reference front() { return cnt.front(); }
+ const_reference front() const { cnt.front(); }
+ reference back() { return cnt.back(); }
+ const_reference back() const { cnt.back(); }
+ void swap(auto_ptrcontainer& x) { cnt.swap(x.cnt); }
+ ~auto_ptrcontainer() {
+ for (iterator i = begin(); i != end(); ++i) {
+ delete *i;
+ }
+ }
+ template <typename Tap> void push_back_ptr(Tap& ap) {
+ cnt.push_back(ap.get());
+ ap.release();
+ }
+ void erase_ptr(iterator i) {
+ delete *i;
+ cnt.erase(i);
+ }
+ reference operator [](size_type n) { return cnt[n]; }
+ const_reference operator [](size_type n) const { return cnt[n]; }
+ void clear() { cnt.clear(); }
+ private:
+ Tcnt cnt;
+};
+*/
+
+};
+
+#endif
+
diff --git a/storage/spider/hs_client/config.cpp b/storage/spider/hs_client/config.cpp
new file mode 100644
index 00000000..0003c3fd
--- /dev/null
+++ b/storage/spider/hs_client/config.cpp
@@ -0,0 +1,292 @@
+
+// vim:sw=2:ai
+
+/*
+ * Copyright (C) 2010-2011 DeNA Co.,Ltd.. All rights reserved.
+ * Copyright (C) 2011-2017 Kentoku SHIBA
+ * See COPYRIGHT.txt for details.
+ */
+
+#include <my_global.h>
+#include "mysql_version.h"
+#if MYSQL_VERSION_ID < 50500
+#include "mysql_priv.h"
+#include <mysql/plugin.h>
+#else
+#include "sql_priv.h"
+#include "probes_mysql.h"
+#endif
+
+#include "config.hpp"
+
+namespace dena {
+
+unsigned int verbose_level = 0;
+
+uchar *
+conf_get_key(
+ conf_param *param,
+ size_t *length,
+ my_bool not_used __attribute__ ((unused))
+) {
+ *length = param->key.length();
+ return (uchar*) param->key.ptr();
+}
+
+config::config()
+{
+ if (my_hash_init(PSI_INSTRUMENT_ME, &conf_hash, &my_charset_bin, 32, 0, 0,
+ (my_hash_get_key) conf_get_key, 0, 0))
+ init = FALSE;
+ else
+ init = TRUE;
+ return;
+}
+
+config::~config()
+{
+ if (init)
+ {
+ conf_param *param;
+ while ((param = (conf_param *) my_hash_element(&conf_hash, 0)))
+ {
+ my_hash_delete(&conf_hash, (uchar*) param);
+ delete param;
+ }
+ my_hash_free(&conf_hash);
+ }
+}
+
+conf_param *
+config::find(const String& key) const
+{
+ if (init)
+ return (conf_param *) my_hash_search(&conf_hash, (const uchar*) key.ptr(),
+ key.length());
+ else
+ return NULL;
+}
+
+conf_param *
+config::find(const char *key) const
+{
+ if (init)
+ return (conf_param *) my_hash_search(&conf_hash, (const uchar*) key,
+ strlen(key));
+ else
+ return NULL;
+}
+
+String
+config::get_str(const String& key, const String& def) const
+{
+ DENA_VERBOSE(30, list_all_params());
+ conf_param *param = find(key);
+ if (!param) {
+ DENA_VERBOSE(10, fprintf(stderr, "CONFIG: %s=%s(default)\n", key.ptr(),
+ def.ptr()));
+ return def;
+ }
+ DENA_VERBOSE(10, fprintf(stderr, "CONFIG: %s=%s\n", key.ptr(),
+ param->val.ptr()));
+ return param->val;
+}
+
+String
+config::get_str(const char *key, const char *def) const
+{
+ DENA_VERBOSE(30, list_all_params());
+ conf_param *param = find(key);
+ if (!param) {
+ DENA_VERBOSE(10, fprintf(stderr, "CONFIG: %s=%s(default)\n", key, def));
+ return String(def, strlen(def), &my_charset_bin);
+ }
+ DENA_VERBOSE(10, fprintf(stderr, "CONFIG: %s=%s\n",
+ key, param->val.ptr()));
+ return param->val;
+}
+
+long long
+config::get_int(const String& key, long long def) const
+{
+ int err;
+ DENA_VERBOSE(30, list_all_params());
+ conf_param *param = find(key);
+ if (!param) {
+ DENA_VERBOSE(10, fprintf(stderr, "CONFIG: %s=%lld(default)\n", key.ptr(),
+ def));
+ return def;
+ }
+ const long long r = my_strtoll10(param->val.ptr(), (char**) NULL, &err);
+ if (err) {
+ DENA_VERBOSE(10, fprintf(stderr, "CONFIG: %s=%lld(err)\n", key.ptr(),
+ def));
+ return def;
+ }
+ DENA_VERBOSE(10, fprintf(stderr, "CONFIG: %s=%lld\n", key.ptr(), r));
+ return r;
+}
+
+long long
+config::get_int(const char *key, long long def) const
+{
+ int err;
+ DENA_VERBOSE(30, list_all_params());
+ conf_param *param = find(key);
+ if (!param) {
+ DENA_VERBOSE(10, fprintf(stderr, "CONFIG: %s=%lld(default)\n", key, def));
+ return def;
+ }
+ const long long r = my_strtoll10(param->val.ptr(), (char**) NULL, &err);
+ if (err) {
+ DENA_VERBOSE(10, fprintf(stderr, "CONFIG: %s=%lld(err)\n", key, def));
+ return def;
+ }
+ DENA_VERBOSE(10, fprintf(stderr, "CONFIG: %s=%lld\n", key, r));
+ return r;
+}
+
+bool
+config::replace(const char *key, const char *val)
+{
+ uint32 val_len = strlen(val);
+ conf_param *param = find(key);
+ if (!param) {
+ /* create */
+ if (!(param = new conf_param()))
+ return TRUE;
+ uint32 key_len = strlen(key);
+ if (
+ param->key.reserve(key_len + 1) ||
+ param->val.reserve(val_len + 1)
+ ) {
+ delete param;
+ return TRUE;
+ }
+ param->key.q_append(key, key_len);
+ param->val.q_append(val, val_len);
+ param->key.c_ptr_safe();
+ param->val.c_ptr_safe();
+ if (my_hash_insert(&conf_hash, (uchar*) param))
+ {
+ delete param;
+ return TRUE;
+ }
+ DENA_VERBOSE(10, fprintf(stderr, "CONFIG: %s=%s(create)\n",
+ param->key.ptr(), param->val.ptr()));
+ return FALSE;
+ }
+ /* replace */
+ param->val.length(0);
+ if (param->val.reserve(val_len + 1))
+ return TRUE;
+ param->val.q_append(val, val_len);
+ param->val.c_ptr_safe();
+ DENA_VERBOSE(10, fprintf(stderr, "CONFIG: %s=%s(replace)\n",
+ param->key.ptr(), param->val.ptr()));
+ return FALSE;
+}
+
+bool
+config::replace(const char *key, long long val)
+{
+ char val_str[22];
+ sprintf(val_str, "%lld", val);
+ return replace(key, val_str);
+}
+
+bool
+config::compare(const char *key, const char *val)
+{
+ conf_param *param = find(key);
+ if (!param)
+ return FALSE;
+ return !strcmp(param->val.ptr(), val);
+}
+
+void
+config::list_all_params() const
+{
+ conf_param *param;
+ DENA_VERBOSE(10, fprintf(stderr, "list_all_params start\n"));
+ for(ulong i = 0; i < conf_hash.records; i++)
+ {
+ if ((param = (conf_param *) my_hash_element((HASH *) &conf_hash, i)))
+ {
+ DENA_VERBOSE(10, fprintf(stderr, "CONFIG: %s=%s\n",
+ param->key.ptr(), param->val.ptr()));
+ }
+ }
+ DENA_VERBOSE(10, fprintf(stderr, "list_all_params end\n"));
+}
+
+config&
+config::operator =(const config& x)
+{
+ DENA_VERBOSE(10, fprintf(stderr, "config operator = start"));
+ if (this != &x && init && x.init) {
+ conf_param *param, *new_param;
+ for(ulong i = 0; i < x.conf_hash.records; i++)
+ {
+ if (
+ (param = (conf_param *) my_hash_element((HASH *) &x.conf_hash, i)) &&
+ (new_param = new conf_param())
+ ) {
+ if (
+ !new_param->key.copy(param->key) &&
+ !new_param->val.copy(param->val)
+ ) {
+ new_param->key.c_ptr_safe();
+ new_param->val.c_ptr_safe();
+ DENA_VERBOSE(10, fprintf(stderr, "CONFIG: %s=%s\n",
+ new_param->key.ptr(), new_param->val.ptr()));
+ if (my_hash_insert(&conf_hash, (uchar*) new_param))
+ delete new_param;
+ } else
+ delete new_param;
+ }
+ }
+ }
+ DENA_VERBOSE(10, fprintf(stderr, "config operator = end %p", this));
+ return *this;
+}
+
+void
+parse_args(int argc, char **argv, config& conf)
+{
+ conf_param *param;
+ for (int i = 1; i < argc; ++i) {
+ const char *const arg = argv[i];
+ const char *const eq = strchr(arg, '=');
+ if (eq == 0) {
+ continue;
+ }
+ if (!(param = new conf_param()))
+ continue;
+ uint32 key_len = (uint32)(eq - arg);
+ uint32 val_len = strlen(eq + 1);
+ if (
+ param->key.reserve(key_len + 1) ||
+ param->val.reserve(val_len + 1)
+ ) {
+ delete param;
+ continue;
+ }
+ param->key.q_append(arg, key_len);
+ param->val.q_append(eq + 1, val_len);
+ param->key.c_ptr_safe();
+ param->val.c_ptr_safe();
+ if (my_hash_insert(&conf.conf_hash, (uchar*) param))
+ {
+ delete param;
+ continue;
+ }
+ }
+ param = conf.find("verbose");
+ if (param) {
+ verbose_level = atoi(param->val.c_ptr());
+ }
+}
+
+};
+
diff --git a/storage/spider/hs_client/config.hpp b/storage/spider/hs_client/config.hpp
new file mode 100644
index 00000000..2880f2f5
--- /dev/null
+++ b/storage/spider/hs_client/config.hpp
@@ -0,0 +1,81 @@
+
+// vim:sw=2:ai
+
+/*
+ * Copyright (C) 2010-2011 DeNA Co.,Ltd.. All rights reserved.
+ * Copyright (C) 2011 Kentoku SHIBA
+ * See COPYRIGHT.txt for details.
+ */
+
+#ifndef DENA_CONFIG_HPP
+#define DENA_CONFIG_HPP
+
+#include "mysql_version.h"
+#if MYSQL_VERSION_ID < 50500
+#include "mysql_priv.h"
+#include <mysql/plugin.h>
+#else
+#include "sql_priv.h"
+#include "probes_mysql.h"
+#include "sql_class.h"
+#endif
+
+#define DENA_VERBOSE(lv, x) if (dena::verbose_level >= (lv)) { (x); }
+
+#ifdef HANDLER_HAS_DIRECT_UPDATE_ROWS
+#define INFO_KIND_HS_RET_FIELDS 1
+#define INFO_KIND_HS_APPEND_STRING_REF 3
+#define INFO_KIND_HS_CLEAR_STRING_REF 4
+#define INFO_KIND_HS_INCREMENT_BEGIN 5
+#define INFO_KIND_HS_INCREMENT_END 6
+#define INFO_KIND_HS_DECREMENT_BEGIN 7
+#define INFO_KIND_HS_DECREMENT_END 8
+#endif
+
+namespace dena {
+
+#ifdef HANDLER_HAS_DIRECT_UPDATE_ROWS
+struct uint32_info {
+ size_t info_size;
+ uint32 *info;
+};
+#endif
+
+struct conf_param {
+ String key;
+ String val;
+};
+
+uchar *conf_get_key(
+ conf_param *share,
+ size_t *length,
+ my_bool not_used __attribute__ ((unused))
+);
+
+struct config {
+ bool init;
+ HASH conf_hash;
+ config();
+ ~config();
+ conf_param *find(const String& key) const;
+ conf_param *find(const char *key) const;
+ String get_str(const String& key, const String& def =
+ String("", &my_charset_bin)) const;
+ String get_str(const char *key, const char *def = "") const;
+ long long get_int(const String& key, long long def = 0) const;
+ long long get_int(const char *key, long long def = 0) const;
+ bool replace(const char *key, const char *val);
+ bool replace(const char *key, long long val);
+ bool compare(const char *key, const char *val);
+ void list_all_params() const;
+ config& operator =(const config& x);
+};
+
+void parse_args(int argc, char **argv, config& conf);
+
+extern unsigned int verbose_level;
+
+};
+
+#endif
+
diff --git a/storage/spider/hs_client/escape.cpp b/storage/spider/hs_client/escape.cpp
new file mode 100644
index 00000000..f3e60afc
--- /dev/null
+++ b/storage/spider/hs_client/escape.cpp
@@ -0,0 +1,129 @@
+
+// vim:sw=2:ai
+
+/*
+ * Copyright (C) 2010 DeNA Co.,Ltd.. All rights reserved.
+ * Copyright (C) 2011-2017 Kentoku SHIBA
+ * See COPYRIGHT.txt for details.
+ */
+
+#include <my_global.h>
+#include "mysql_version.h"
+#include "hs_compat.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
+read_ui32(char *& start, char *finish)
+{
+ char *const n_begin = start;
+ read_token(start, finish);
+ char *const n_end = start;
+ uint32 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 v)
+{
+ char *wp = buf.make_space(12);
+ int len = snprintf(wp, 12, "%u", v);
+ if (len > 0) {
+ buf.space_wrote(len);
+ }
+}
+
+void
+write_ui64(string_buffer& buf, uint64 v)
+{
+ char *wp = buf.make_space(22);
+ int len = snprintf(wp, 22, "%llu", static_cast<unsigned long long>(v));
+ if (len > 0) {
+ buf.space_wrote(len);
+ }
+}
+
+};
+
diff --git a/storage/spider/hs_client/escape.hpp b/storage/spider/hs_client/escape.hpp
new file mode 100644
index 00000000..4c23e167
--- /dev/null
+++ b/storage/spider/hs_client/escape.hpp
@@ -0,0 +1,64 @@
+
+// vim:sw=2:ai
+
+/*
+ * Copyright (C) 2010 DeNA Co.,Ltd.. All rights reserved.
+ * See COPYRIGHT.txt for details.
+ */
+
+#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 read_ui32(char *& start, char *finish);
+void write_ui32(string_buffer& buf, uint32 v);
+void write_ui64(string_buffer& buf, uint64 v);
+
+inline bool
+is_null_expression(const char *start, const char *finish)
+{
+ return (finish == start + 1 && start[0] == 0);
+}
+
+inline void
+read_token(char *& start, char *finish)
+{
+ char *const p = memchr_char(start, '\t', finish - start);
+ if (p == 0) {
+ start = finish;
+ } else {
+ start = p;
+ }
+}
+
+inline void
+skip_token_delim_fold(char *& start, char *finish)
+{
+ while (start != finish && start[0] == '\t') {
+ ++start;
+ }
+}
+
+inline void
+skip_one(char *& start, char *finish)
+{
+ if (start != finish) {
+ ++start;
+ }
+}
+
+};
+
+#endif
+
diff --git a/storage/spider/hs_client/fatal.cpp b/storage/spider/hs_client/fatal.cpp
new file mode 100644
index 00000000..cfbc14df
--- /dev/null
+++ b/storage/spider/hs_client/fatal.cpp
@@ -0,0 +1,49 @@
+
+// vim:sw=2:ai
+
+/*
+ * Copyright (C) 2010-2011 DeNA Co.,Ltd.. All rights reserved.
+ * Copyright (C) 2011-2017 Kentoku SHIBA
+ * See COPYRIGHT.txt for details.
+ */
+
+#include <my_global.h>
+#include "mysql_version.h"
+#if MYSQL_VERSION_ID < 50500
+#include "mysql_priv.h"
+#include <mysql/plugin.h>
+#else
+#include "sql_priv.h"
+#include "probes_mysql.h"
+#endif
+
+#include "fatal.hpp"
+
+namespace dena {
+
+/*
+const int opt_syslog = LOG_ERR | LOG_PID | LOG_CONS;
+*/
+
+void
+fatal_abort(const String& message)
+{
+ fprintf(stderr, "FATAL_COREDUMP: %s\n", message.ptr());
+/*
+ syslog(opt_syslog, "FATAL_COREDUMP: %s", message.ptr());
+*/
+ abort();
+}
+
+void
+fatal_abort(const char *message)
+{
+ fprintf(stderr, "FATAL_COREDUMP: %s\n", message);
+/*
+ syslog(opt_syslog, "FATAL_COREDUMP: %s", message);
+*/
+ abort();
+}
+
+};
+
diff --git a/storage/spider/hs_client/fatal.hpp b/storage/spider/hs_client/fatal.hpp
new file mode 100644
index 00000000..38fc149e
--- /dev/null
+++ b/storage/spider/hs_client/fatal.hpp
@@ -0,0 +1,31 @@
+
+// vim:sw=2:ai
+
+/*
+ * Copyright (C) 2010-2011 DeNA Co.,Ltd.. All rights reserved.
+ * Copyright (C) 2011-2017 Kentoku SHIBA
+ * See COPYRIGHT.txt for details.
+ */
+
+#ifndef DENA_FATAL_HPP
+#define DENA_FATAL_HPP
+
+#include "mysql_version.h"
+#if MYSQL_VERSION_ID < 50500
+#include "mysql_priv.h"
+#include <mysql/plugin.h>
+#else
+#include "sql_priv.h"
+#include "probes_mysql.h"
+#include "sql_class.h"
+#endif
+
+namespace dena {
+
+void fatal_abort(const String& message);
+void fatal_abort(const char *message);
+
+};
+
+#endif
+
diff --git a/storage/spider/hs_client/hs_compat.h b/storage/spider/hs_client/hs_compat.h
new file mode 100644
index 00000000..fb9b02ad
--- /dev/null
+++ b/storage/spider/hs_client/hs_compat.h
@@ -0,0 +1,41 @@
+/* Copyright (C) 2013-2018 Kentoku Shiba
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */
+
+#ifndef HS_COMPAT_H
+#define HS_COMPAT_H
+
+#if defined(MARIADB_BASE_VERSION) && MYSQL_VERSION_ID >= 100500
+#define SPD_INIT_DYNAMIC_ARRAY2(A, B, C, D, E, F) \
+ my_init_dynamic_array2(PSI_INSTRUMENT_ME, A, B, C, D, E, F)
+#define SPD_INIT_ALLOC_ROOT(A, B, C, D) \
+ init_alloc_root(PSI_INSTRUMENT_ME, A, B, C, D)
+#elif defined(MARIADB_BASE_VERSION) && MYSQL_VERSION_ID >= 100213
+#define SPD_INIT_DYNAMIC_ARRAY2(A, B, C, D, E, F) \
+ my_init_dynamic_array2(A, B, C, D, E, F)
+#define SPD_INIT_ALLOC_ROOT(A, B, C, D) \
+ init_alloc_root(A, "spider", B, C, D)
+#elif defined(MARIADB_BASE_VERSION) && MYSQL_VERSION_ID >= 100000
+#define SPD_INIT_DYNAMIC_ARRAY2(A, B, C, D, E, F) \
+ my_init_dynamic_array2(A, B, C, D, E, F)
+#define SPD_INIT_ALLOC_ROOT(A, B, C, D) \
+ init_alloc_root(A, B, C, D)
+#else
+#define SPD_INIT_DYNAMIC_ARRAY2(A, B, C, D, E, F) \
+ my_init_dynamic_array2(A, B, C, D, E)
+#define SPD_INIT_ALLOC_ROOT(A, B, C, D) \
+ init_alloc_root(A, B, C)
+#endif
+
+#endif
diff --git a/storage/spider/hs_client/hstcpcli.cpp b/storage/spider/hs_client/hstcpcli.cpp
new file mode 100644
index 00000000..4c93b5a3
--- /dev/null
+++ b/storage/spider/hs_client/hstcpcli.cpp
@@ -0,0 +1,667 @@
+
+// vim:sw=2:ai
+
+/*
+ * Copyright (C) 2010-2011 DeNA Co.,Ltd.. All rights reserved.
+ * Copyright (C) 2011-2017 Kentoku SHIBA
+ * See COPYRIGHT.txt for details.
+ */
+
+#include <my_global.h>
+#include "mysql_version.h"
+#include "hs_compat.h"
+#if MYSQL_VERSION_ID < 50500
+#include "mysql_priv.h"
+#include <mysql/plugin.h>
+#else
+#include "sql_priv.h"
+#include "probes_mysql.h"
+#include "sql_class.h"
+#endif
+
+#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 {
+
+hstresult::hstresult()
+{
+ SPD_INIT_DYNAMIC_ARRAY2(&flds, sizeof(string_ref), NULL, 16, 16,
+ MYF(MY_WME));
+}
+
+hstresult::~hstresult()
+{
+ delete_dynamic(&flds);
+}
+
+struct hstcpcli : public hstcpcli_i, private noncopyable {
+ hstcpcli(const socket_args& args);
+ virtual ~hstcpcli();
+ 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);
+ virtual void request_buf_auth(const char *secret, const char *typ);
+ virtual void request_buf_exec_generic(size_t pst_id, const string_ref& op,
+ const string_ref *kvs, size_t kvslen, uint32 limit, uint32 skip,
+ const string_ref& mod_op, const string_ref *mvs, size_t mvslen,
+ const hstcpcli_filter *fils, size_t filslen, int invalues_keypart,
+ const string_ref *invalues, size_t invalueslen);
+ virtual size_t request_buf_append(const char *start, const char *finish);
+ virtual void request_reset();
+ virtual int request_send();
+ virtual int response_recv(size_t& num_flds_r);
+ virtual int get_result(hstresult& result);
+ virtual const string_ref *get_next_row();
+ virtual const string_ref *get_next_row_from_result(hstresult& result);
+ virtual size_t get_row_size();
+ virtual size_t get_row_size_from_result(hstresult& result);
+ virtual void response_buf_remove();
+ virtual int get_error_code();
+ virtual String& get_error();
+ virtual void clear_error();
+ virtual int set_timeout(int send_timeout, int recv_timeout);
+ virtual size_t get_num_req_bufd() { return num_req_bufd; }
+ virtual size_t get_num_req_sent() { return num_req_sent; }
+ virtual size_t get_num_req_rcvd() { return num_req_rcvd; }
+ virtual size_t get_response_end_offset() { return response_end_offset; }
+ virtual const char *get_readbuf_begin() { return readbuf.begin(); }
+ virtual const char *get_readbuf_end() { return readbuf.end(); }
+ virtual const char *get_writebuf_begin() { return writebuf.begin(); }
+ virtual size_t get_writebuf_size() { return writebuf.size(); }
+ virtual void write_error_to_log(const char *func_name, const char *file_name,
+ ulong line_no);
+ private:
+ int read_more();
+ int set_error(int code, const String& str);
+ int set_error(int code, const char *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 cur_row_size;
+ 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;
+ String error_str;
+ DYNAMIC_ARRAY flds;
+ int errno_buf;
+};
+
+hstcpcli::hstcpcli(const socket_args& args)
+ : sargs(args), response_end_offset(0), cur_row_offset(0), cur_row_size(0),
+ num_flds(0), num_req_bufd(0), num_req_sent(0), num_req_rcvd(0),
+ error_code(0), errno_buf(0)
+{
+ String err;
+ SPD_INIT_DYNAMIC_ARRAY2(&flds, sizeof(string_ref), NULL, 16, 16, MYF(MY_WME));
+ if (socket_connect(fd, sargs, err) != 0) {
+ set_error(-1, err);
+ }
+}
+
+hstcpcli::~hstcpcli()
+{
+ delete_dynamic(&flds);
+}
+
+void
+hstcpcli::close()
+{
+ fd.close();
+ readbuf.clear();
+ writebuf.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();
+ String err;
+ if (socket_connect(fd, sargs, err) != 0) {
+ set_error(-1, err);
+ }
+ return error_code;
+}
+
+int
+hstcpcli::set_timeout(int send_timeout, int recv_timeout)
+{
+ String err;
+ sargs.send_timeout = send_timeout;
+ sargs.recv_timeout = recv_timeout;
+ if (socket_set_timeout(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;
+}
+
+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);
+ int rlen;
+ errno = 0;
+ while ((rlen = read(fd.get(), wp, block_size)) <= 0) {
+ errno_buf = errno;
+ if (rlen < 0) {
+ if (errno == EINTR || errno == EAGAIN)
+ {
+ errno = 0;
+ continue;
+ }
+ error_str = String("read: failed", &my_charset_bin);
+ } else {
+ error_str = String("read: eof", &my_charset_bin);
+ }
+ 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.length(0);
+}
+
+int
+hstcpcli::set_error(int code, const String& str)
+{
+ DBG(fprintf(stderr, "SET_ERROR: %d\n", code));
+ error_code = code;
+ error_str = str;
+ return error_code;
+}
+
+int
+hstcpcli::set_error(int code, const char *str)
+{
+ uint32 str_len = strlen(str);
+ DBG(fprintf(stderr, "SET_ERROR: %d\n", code));
+ error_code = code;
+ error_str.length(0);
+ if (error_str.reserve(str_len + 1))
+ return 0;
+ error_str.q_append(str, str_len);
+ error_str.c_ptr_safe();
+ 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) {
+*/
+ if (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;
+}
+
+void
+hstcpcli::request_buf_auth(const char *secret, const char *typ)
+{
+/*
+ if (num_req_sent > 0 || num_req_rcvd > 0) {
+*/
+ if (num_req_rcvd > 0) {
+ close();
+ set_error(-1, "request_buf_auth: protocol out of sync");
+ return;
+ }
+ if (typ == 0) {
+ typ = "1";
+ }
+ const string_ref typ_ref(typ, strlen(typ));
+ const string_ref secret_ref(secret, strlen(secret));
+ writebuf.append_literal("A\t");
+ writebuf.append(typ_ref.begin(), typ_ref.end());
+ writebuf.append_literal("\t");
+ writebuf.append(secret_ref.begin(), secret_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 limit, uint32 skip,
+ const string_ref& mod_op, const string_ref *mvs, size_t mvslen,
+ const hstcpcli_filter *fils, size_t filslen, int invalues_keypart,
+ const string_ref *invalues, size_t invalueslen)
+{
+/*
+ if (num_req_sent > 0 || num_req_rcvd > 0) {
+*/
+ if (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 || invalues_keypart >= 0 ||
+ mod_op.size() != 0 || filslen != 0) {
+ /* has more option */
+ writebuf.append_literal("\t");
+ append_uint32(writebuf, limit); // FIXME size_t ?
+ if (skip != 0 || invalues_keypart >= 0 ||
+ mod_op.size() != 0 || filslen != 0) {
+ writebuf.append_literal("\t");
+ append_uint32(writebuf, skip); // FIXME size_t ?
+ }
+ if (invalues_keypart >= 0) {
+ writebuf.append_literal("\t@\t");
+ append_uint32(writebuf, invalues_keypart);
+ writebuf.append_literal("\t");
+ append_uint32(writebuf, invalueslen);
+ for (size_t i = 0; i < invalueslen; ++i) {
+ const string_ref& s = invalues[i];
+ append_delim_value(writebuf, s.begin(), s.end());
+ }
+ }
+ 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;
+}
+
+size_t
+hstcpcli::request_buf_append(const char *start, const char *finish)
+{
+/*
+ if (num_req_sent > 0 || num_req_rcvd > 0) {
+*/
+ if (num_req_rcvd > 0) {
+ close();
+ set_error(-1, "request_buf_append: protocol out of sync");
+ return 0;
+ }
+ const char *nl = start;
+ size_t num_req = 0;
+ while ((nl = memchr_char(nl, '\n', finish - nl))) {
+ if (nl == finish)
+ break;
+ num_req++;
+ nl++;
+ }
+ num_req++;
+ writebuf.append(start, finish);
+ if (*(finish - 1) != '\n')
+ writebuf.append_literal("\n");
+ num_req_bufd += num_req;
+ return num_req;
+}
+
+void
+hstcpcli::request_reset()
+{
+ if (num_req_bufd) {
+ writebuf.erase_front(writebuf.size());
+ num_req_bufd = 0;
+ }
+}
+
+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) {
+*/
+ if (num_req_bufd == 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();
+ if (lbegin < lend)
+ {
+ const char *const nl = memchr_char(lbegin, '\n', lend - lbegin);
+ if (nl != 0) {
+ offset += (nl + 1) - lbegin;
+ break;
+ }
+ offset += lend - lbegin;
+ }
+ if (read_more() <= 0) {
+ close();
+ error_code = -1;
+ return error_code;
+ }
+ }
+ 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;
+ String e = String(err_begin, (uint32)(err_end - err_begin), &my_charset_bin);
+ if (!e.length()) {
+ e = String("unknown_error", &my_charset_bin);
+ }
+ return set_error(resp_code, e);
+ }
+ cur_row_size = 0;
+ cur_row_offset = start - readbuf.begin();
+ DBG(fprintf(stderr, "[%s] ro=%zu eol=%zu\n",
+ String(readbuf.begin(), readbuf.begin() + response_end_offset)
+ .c_str(),
+ cur_row_offset, response_end_offset));
+ DBG(fprintf(stderr, "RES 0\n"));
+ if (flds.max_element < num_flds)
+ {
+ if (allocate_dynamic(&flds, num_flds))
+ return set_error(-1, "out of memory");
+ }
+ flds.elements = num_flds;
+ return 0;
+}
+
+int
+hstcpcli::get_result(hstresult& result)
+{
+/*
+ readbuf.swap(result.readbuf);
+*/
+ char *const wp = result.readbuf.make_space(response_end_offset);
+ memcpy(wp, readbuf.begin(), response_end_offset);
+ result.readbuf.space_wrote(response_end_offset);
+ result.response_end_offset = response_end_offset;
+ result.num_flds = num_flds;
+ result.cur_row_size = cur_row_size;
+ result.cur_row_offset = cur_row_offset;
+ if (result.flds.max_element < num_flds)
+ {
+ if (allocate_dynamic(&result.flds, num_flds))
+ return set_error(-1, "out of memory");
+ }
+ result.flds.elements = num_flds;
+ return 0;
+}
+
+const string_ref *
+hstcpcli::get_next_row()
+{
+ if (num_flds == 0 || flds.elements < num_flds) {
+ DBG(fprintf(stderr, "GNR NF 0\n"));
+ return 0;
+ }
+ 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 */
+ ((string_ref *) flds.buffer)[i] = string_ref();
+ } else {
+ unescape_string(wp, fld_begin, fld_end); /* in-place */
+ ((string_ref *) flds.buffer)[i] = string_ref(fld_begin, wp);
+ }
+ }
+ cur_row_size = start - (readbuf.begin() + cur_row_offset);
+ cur_row_offset = start - readbuf.begin();
+ return (string_ref *) flds.buffer;
+}
+
+const string_ref *
+hstcpcli::get_next_row_from_result(hstresult& result)
+{
+ if (result.num_flds == 0 || result.flds.elements < result.num_flds) {
+ DBG(fprintf(stderr, "GNR NF 0\n"));
+ return 0;
+ }
+ char *start = result.readbuf.begin() + result.cur_row_offset;
+ char *const finish = result.readbuf.begin() + result.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 < result.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 */
+ ((string_ref *) result.flds.buffer)[i] = string_ref();
+ } else {
+ unescape_string(wp, fld_begin, fld_end); /* in-place */
+ ((string_ref *) result.flds.buffer)[i] = string_ref(fld_begin, wp);
+ }
+ }
+ result.cur_row_size =
+ start - (result.readbuf.begin() + result.cur_row_offset);
+ result.cur_row_offset = start - result.readbuf.begin();
+ return (string_ref *) result.flds.buffer;
+}
+
+size_t
+hstcpcli::get_row_size()
+{
+ return cur_row_size;
+}
+
+size_t
+hstcpcli::get_row_size_from_result(hstresult& result)
+{
+ return result.cur_row_size;
+}
+
+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;
+}
+
+void
+hstcpcli::write_error_to_log(
+ const char *func_name,
+ const char *file_name,
+ ulong line_no
+) {
+ if (errno_buf) {
+ time_t cur_time = (time_t) time((time_t*) 0);
+ struct tm lt;
+ struct tm *l_time = localtime_r(&cur_time, &lt);
+ fprintf(stderr,
+ "%04d%02d%02d %02d:%02d:%02d [ERROR] hstcpcli: [%d][%s]"
+ " [%s][%s][%lu] errno=%d\n",
+ l_time->tm_year + 1900, l_time->tm_mon + 1, l_time->tm_mday,
+ l_time->tm_hour, l_time->tm_min, l_time->tm_sec,
+ error_code, error_str.c_ptr_safe(),
+ func_name, file_name, line_no, errno_buf);
+ }
+}
+
+hstcpcli_ptr
+hstcpcli_i::create(const socket_args& args)
+{
+ return hstcpcli_ptr(new hstcpcli(args));
+}
+
+};
+
diff --git a/storage/spider/hs_client/hstcpcli.hpp b/storage/spider/hs_client/hstcpcli.hpp
new file mode 100644
index 00000000..6894716e
--- /dev/null
+++ b/storage/spider/hs_client/hstcpcli.hpp
@@ -0,0 +1,98 @@
+
+// vim:sw=2:ai
+
+/*
+ * Copyright (C) 2010-2011 DeNA Co.,Ltd.. All rights reserved.
+ * Copyright (C) 2011 Kentoku SHIBA
+ * See COPYRIGHT.txt for details.
+ */
+
+#ifndef DENA_HSTCPCLI_HPP
+#define DENA_HSTCPCLI_HPP
+
+#define HANDLERSOCKET_MYSQL_UTIL 1
+
+#include "mysql_version.h"
+#if MYSQL_VERSION_ID < 50500
+#include "mysql_priv.h"
+#include <mysql/plugin.h>
+#else
+#include "sql_priv.h"
+#include "probes_mysql.h"
+#endif
+
+#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 hstcpcli_i *hstcpcli_ptr;
+
+struct hstresult {
+ hstresult();
+ virtual ~hstresult();
+ string_buffer readbuf;
+ size_t response_end_offset;
+ size_t num_flds;
+ size_t cur_row_offset;
+ size_t cur_row_size;
+ DYNAMIC_ARRAY flds;
+};
+
+struct hstcpcli_i {
+ virtual ~hstcpcli_i() { }
+ virtual void close() = 0;
+ virtual int reconnect() = 0;
+ virtual bool stable_point() = 0;
+ virtual void request_buf_auth(const char *secret, const char *typ) = 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 limit, uint32 skip,
+ const string_ref& mod_op, const string_ref *mvs, size_t mvslen,
+ const hstcpcli_filter *fils = 0, size_t filslen = 0,
+ int invalues_keypart = -1, const string_ref *invalues = 0,
+ size_t invalueslen = 0) = 0; // FIXME: too long
+ virtual size_t request_buf_append(const char *start, const char *finish) = 0;
+ virtual void request_reset() = 0;
+ virtual int request_send() = 0;
+ virtual int response_recv(size_t& num_flds_r) = 0;
+ virtual int get_result(hstresult& result) = 0;
+ virtual const string_ref *get_next_row() = 0;
+ virtual const string_ref *get_next_row_from_result(hstresult& result) = 0;
+ virtual size_t get_row_size() = 0;
+ virtual size_t get_row_size_from_result(hstresult& result) = 0;
+ virtual void response_buf_remove() = 0;
+ virtual int get_error_code() = 0;
+ virtual String& get_error() = 0;
+ virtual void clear_error() = 0;
+ virtual int set_timeout(int send_timeout, int recv_timeout) = 0;
+ virtual size_t get_num_req_bufd() = 0;
+ virtual size_t get_num_req_sent() = 0;
+ virtual size_t get_num_req_rcvd() = 0;
+ virtual size_t get_response_end_offset() = 0;
+ virtual const char *get_readbuf_begin() = 0;
+ virtual const char *get_readbuf_end() = 0;
+ virtual const char *get_writebuf_begin() = 0;
+ virtual size_t get_writebuf_size() = 0;
+ virtual void write_error_to_log(const char *func_name, const char *file_name,
+ ulong line_no) = 0;
+ static hstcpcli_ptr create(const socket_args& args);
+};
+
+};
+
+#endif
+
diff --git a/storage/spider/hs_client/mutex.hpp b/storage/spider/hs_client/mutex.hpp
new file mode 100644
index 00000000..8e331fb6
--- /dev/null
+++ b/storage/spider/hs_client/mutex.hpp
@@ -0,0 +1,48 @@
+
+// vim:sw=2:ai
+
+/*
+ * Copyright (C) 2010-2011 DeNA Co.,Ltd.. All rights reserved.
+ * See COPYRIGHT.txt for details.
+ */
+
+#ifndef DENA_MUTEX_HPP
+#define DENA_MUTEX_HPP
+
+#include "fatal.hpp"
+#include "util.hpp"
+
+namespace dena {
+
+struct condition;
+
+struct mutex : private noncopyable {
+ friend struct condition;
+ mutex() {
+ if (pthread_mutex_init(&mtx, 0) != 0) {
+ fatal_abort("pthread_mutex_init");
+ }
+ }
+ ~mutex() {
+ if (pthread_mutex_destroy(&mtx) != 0) {
+ fatal_abort("pthread_mutex_destroy");
+ }
+ }
+ void lock() const {
+ if (pthread_mutex_lock(&mtx) != 0) {
+ fatal_abort("pthread_mutex_lock");
+ }
+ }
+ void unlock() const {
+ if (pthread_mutex_unlock(&mtx) != 0) {
+ fatal_abort("pthread_mutex_unlock");
+ }
+ }
+ private:
+ mutable pthread_mutex_t mtx;
+};
+
+};
+
+#endif
+
diff --git a/storage/spider/hs_client/socket.cpp b/storage/spider/hs_client/socket.cpp
new file mode 100644
index 00000000..45b8100e
--- /dev/null
+++ b/storage/spider/hs_client/socket.cpp
@@ -0,0 +1,313 @@
+
+// vim:sw=2:ai
+
+/*
+ * Copyright (C) 2010-2011 DeNA Co.,Ltd.. All rights reserved.
+ * Copyright (C) 2011-2017 Kentoku SHIBA
+ * See COPYRIGHT.txt for details.
+ */
+
+#include <my_global.h>
+#include <my_config.h>
+#ifndef __WIN__
+#include <sys/types.h>
+#include <sys/un.h>
+#endif
+
+#include "mysql_version.h"
+#include "hs_compat.h"
+#if MYSQL_VERSION_ID < 50500
+#include "mysql_priv.h"
+#include <mysql/plugin.h>
+#else
+#if defined(MARIADB_BASE_VERSION) && MYSQL_VERSION_ID >= 100000
+#include <my_global.h>
+#endif
+#include "sql_priv.h"
+#include "probes_mysql.h"
+#endif
+
+#include "socket.hpp"
+#include "string_util.hpp"
+#include "fatal.hpp"
+
+/*
+struct sockaddr_un {
+ short sun_family;
+ char sun_path[108];
+};
+*/
+
+namespace dena {
+
+void
+ignore_sigpipe()
+{
+#if defined(SIGPIPE) && !defined(__WIN__)
+ if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) {
+ fatal_abort("SIGPIPE SIG_IGN");
+ }
+#endif
+}
+
+void
+socket_args::set(const config& conf)
+{
+ timeout = (int) conf.get_int("timeout", 600);
+ listen_backlog = (int) conf.get_int("listen_backlog", 256);
+ String node = conf.get_str("host", "");
+ String port = conf.get_str("port", "");
+ if (node.length() || port.length()) {
+ if (family == AF_UNIX || !strcmp(node.c_ptr(), "/")) {
+ set_unix_domain(port.c_ptr());
+ } else {
+ const char *nd = !node.length() ? 0 : node.c_ptr();
+ if (resolve(nd, port.c_ptr()) != 0) {
+ String message("getaddrinfo failed: ", &my_charset_bin);
+ message.reserve(node.length() + sizeof(":") - 1 + port.length());
+ message.append(node);
+ message.q_append(":", sizeof(":") - 1);
+ message.append(port);
+ fatal_abort(message);
+ }
+ }
+ }
+ sndbuf = (int) conf.get_int("sndbuf", 0);
+ rcvbuf = (int) conf.get_int("rcvbuf", 0);
+}
+
+void
+socket_args::set_unix_domain(const char *path)
+{
+#ifndef __WIN__
+ 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);
+#endif
+}
+
+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_timeout(auto_file& fd, const socket_args& args, String& err_r)
+{
+ if (!args.nonblocking) {
+#if defined(SO_SNDTIMEO) && defined(SO_RCVTIMEO)
+ if (args.recv_timeout != 0) {
+#ifndef __WIN__
+ struct timeval tv;
+ tv.tv_sec = args.recv_timeout;
+ tv.tv_usec = 0;
+#else
+ int tv = args.recv_timeout * 1000;
+#endif
+ if (setsockopt(fd.get(), SOL_SOCKET, SO_RCVTIMEO,
+#ifndef __WIN__
+ (const void *) &tv,
+#else
+ (const char *) &tv,
+#endif
+ sizeof(tv)) != 0) {
+ return errno_string("setsockopt SO_RCVTIMEO", errno, err_r);
+ }
+ }
+ if (args.send_timeout != 0) {
+#ifndef __WIN__
+ struct timeval tv;
+ tv.tv_sec = args.send_timeout;
+ tv.tv_usec = 0;
+#else
+ int tv = args.send_timeout * 1000;
+#endif
+ if (setsockopt(fd.get(), SOL_SOCKET, SO_SNDTIMEO,
+#ifndef __WIN__
+ (const void *) &tv,
+#else
+ (const char *) &tv,
+#endif
+ sizeof(tv)) != 0) {
+ return errno_string("setsockopt SO_SNDTIMEO", errno, err_r);
+ }
+ }
+#endif
+ }
+ return 0;
+}
+
+int
+socket_set_options(auto_file& fd, const socket_args& args, String& err_r)
+{
+ if (args.timeout != 0 && !args.nonblocking) {
+#if defined(SO_SNDTIMEO) && defined(SO_RCVTIMEO)
+#ifndef __WIN__
+ struct timeval tv;
+ tv.tv_sec = args.timeout;
+ tv.tv_usec = 0;
+#else
+ int tv = args.timeout * 1000;
+#endif
+ if (setsockopt(fd.get(), SOL_SOCKET, SO_RCVTIMEO,
+#ifndef __WIN__
+ (const void *) &tv,
+#else
+ (const char *) &tv,
+#endif
+ sizeof(tv)) != 0) {
+ return errno_string("setsockopt SO_RCVTIMEO", errno, err_r);
+ }
+#ifndef __WIN__
+ tv.tv_sec = args.timeout;
+ tv.tv_usec = 0;
+#else
+ tv = args.timeout * 1000;
+#endif
+ if (setsockopt(fd.get(), SOL_SOCKET, SO_SNDTIMEO,
+#ifndef __WIN__
+ (const void *) &tv,
+#else
+ (const char *) &tv,
+#endif
+ sizeof(tv)) != 0) {
+ return errno_string("setsockopt SO_RCVTIMEO", errno, err_r);
+ }
+#endif
+ }
+#ifndef __WIN__
+ if (args.nonblocking && fcntl(fd.get(), F_SETFL, O_NONBLOCK) != 0) {
+ return errno_string("fcntl O_NONBLOCK", errno, err_r);
+ }
+#endif
+ if (args.sndbuf != 0) {
+ const int v = args.sndbuf;
+ if (setsockopt(fd.get(), SOL_SOCKET, SO_SNDBUF,
+#ifndef __WIN__
+ (const void *) &v,
+#else
+ (const char *) &v,
+#endif
+ 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,
+#ifndef __WIN__
+ (const void *) &v,
+#else
+ (const char *) &v,
+#endif
+ sizeof(v)) != 0) {
+ return errno_string("setsockopt SO_RCVBUF", errno, err_r);
+ }
+ }
+ return 0;
+}
+
+int
+socket_open(auto_file& fd, const socket_args& args, String& err_r)
+{
+ fd.reset((int) 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, 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
+#ifndef __WIN__
+ || errno != EINPROGRESS
+#endif
+ ) {
+ return errno_string("connect", errno, err_r);
+ }
+ }
+ return 0;
+}
+
+int
+socket_bind(auto_file& fd, const socket_args& args, String& err_r)
+{
+ fd.reset((int) socket(args.family, args.socktype, args.protocol));
+ if (fd.get() < 0) {
+ return errno_string("socket", errno, err_r);
+ }
+ if (args.reuseaddr) {
+#ifndef __WIN__
+ 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 {
+#endif
+ int v = 1;
+ if (setsockopt(fd.get(), SOL_SOCKET, SO_REUSEADDR,
+#ifndef __WIN__
+ (const void *) &v,
+#else
+ (const char *) &v,
+#endif
+ sizeof(v)) != 0) {
+ return errno_string("setsockopt SO_REUSEADDR", errno, err_r);
+ }
+#ifndef __WIN__
+ }
+#endif
+ }
+ 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);
+ }
+#ifndef __WIN__
+ if (args.nonblocking && fcntl(fd.get(), F_SETFL, O_NONBLOCK) != 0) {
+ return errno_string("fcntl O_NONBLOCK", errno, err_r);
+ }
+#endif
+ return 0;
+}
+
+int
+socket_accept(int listen_fd, auto_file& fd, const socket_args& args,
+ sockaddr_storage& addr_r, socklen_t& addrlen_r, String& err_r)
+{
+ fd.reset((int) accept(listen_fd, reinterpret_cast<sockaddr *>(&addr_r),
+ &addrlen_r));
+ if (fd.get() < 0) {
+ return errno_string("accept", errno, err_r);
+ }
+ return socket_set_options(fd, args, err_r);
+}
+
+};
+
diff --git a/storage/spider/hs_client/socket.hpp b/storage/spider/hs_client/socket.hpp
new file mode 100644
index 00000000..a3e6527a
--- /dev/null
+++ b/storage/spider/hs_client/socket.hpp
@@ -0,0 +1,62 @@
+
+// vim:sw=2:ai
+
+/*
+ * Copyright (C) 2010-2011 DeNA Co.,Ltd.. All rights reserved.
+ * Copyright (C) 2011 Kentoku SHIBA
+ * See COPYRIGHT.txt for details.
+ */
+
+#ifndef DENA_SOCKET_HPP
+#define DENA_SOCKET_HPP
+
+#include "mysql_version.h"
+#if MYSQL_VERSION_ID < 50500
+#include "mysql_priv.h"
+#include <mysql/plugin.h>
+#else
+#include "sql_priv.h"
+#include "probes_mysql.h"
+#endif
+
+#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 send_timeout;
+ int recv_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), send_timeout(600), recv_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_set_timeout(auto_file& fd, const socket_args& args, String& err_r);
+int socket_bind(auto_file& fd, const socket_args& args, String& err_r);
+int socket_connect(auto_file& fd, const socket_args& args, String& err_r);
+int socket_accept(int listen_fd, auto_file& fd, const socket_args& args,
+ sockaddr_storage& addr_r, socklen_t& addrlen_r, String& err_r);
+
+};
+
+#endif
+
diff --git a/storage/spider/hs_client/string_buffer.hpp b/storage/spider/hs_client/string_buffer.hpp
new file mode 100644
index 00000000..c9a60748
--- /dev/null
+++ b/storage/spider/hs_client/string_buffer.hpp
@@ -0,0 +1,146 @@
+
+// vim:sw=2:ai
+
+/*
+ * Copyright (C) 2010-2011 DeNA Co.,Ltd.. All rights reserved.
+ * Copyright (C) 2011 Kentoku SHIBA
+ * See COPYRIGHT.txt for details.
+ */
+
+#ifndef DENA_STRING_BUFFER_HPP
+#define DENA_STRING_BUFFER_HPP
+
+/*
+#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() {
+ real_free();
+ }
+ void real_free() {
+ if (alloc_size) {
+ DENA_FREE(buffer);
+ buffer = 0;
+ begin_offset = 0;
+ end_offset = 0;
+ alloc_size = 0;
+ }
+ }
+ size_t real_size() {
+ return alloc_size;
+ }
+ 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 = len < alloc_size - end_offset ? len : alloc_size - end_offset;
+ end_offset += len;
+ }
+ 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;
+ }
+ void swap(string_buffer& sb) {
+ char *tmp_buffer = buffer;
+ size_t tmp_begin_offset = begin_offset;
+ size_t tmp_end_offset = end_offset;
+ size_t tmp_alloc_size = alloc_size;
+ buffer = sb.buffer;
+ begin_offset = sb.begin_offset;
+ end_offset = sb.end_offset;
+ alloc_size = sb.alloc_size;
+ sb.buffer = tmp_buffer;
+ sb.begin_offset = tmp_begin_offset;
+ sb.end_offset = tmp_end_offset;
+ sb.alloc_size = tmp_alloc_size;
+ }
+ private:
+ char *buffer;
+ size_t begin_offset;
+ size_t end_offset;
+ size_t alloc_size;
+};
+
+};
+
+#endif
+
diff --git a/storage/spider/hs_client/string_ref.hpp b/storage/spider/hs_client/string_ref.hpp
new file mode 100644
index 00000000..028c4146
--- /dev/null
+++ b/storage/spider/hs_client/string_ref.hpp
@@ -0,0 +1,106 @@
+
+// vim:sw=2:ai
+
+/*
+ * Copyright (C) 2010-2011 DeNA Co.,Ltd.. All rights reserved.
+ * Copyright (C) 2011 Kentoku SHIBA
+ * See COPYRIGHT.txt for details.
+ */
+
+#ifndef DENA_STRING_REF_HPP
+#define DENA_STRING_REF_HPP
+
+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; }
+ void set(const char *s, size_t len) { start = s; length = len; }
+ void set(const char *s, const char *f) { start = s; length = f - s; }
+ 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);
+}
+
+struct string_ref_list_wrap {
+ string_ref_list_wrap() {
+ if (SPD_INIT_DYNAMIC_ARRAY2(&string_ref_list, sizeof(string_ref),
+ NULL, 16, 16, MYF(MY_WME)))
+ string_ref_list_init = FALSE;
+ else
+ string_ref_list_init = TRUE;
+ }
+ virtual ~string_ref_list_wrap() {
+ if (string_ref_list_init) delete_dynamic(&string_ref_list); }
+ void clear() {
+ if (string_ref_list_init) string_ref_list.elements = 0; }
+ void push_back(string_ref &e) {
+ if (string_ref_list_init) insert_dynamic(&string_ref_list, (uchar*) &e);
+ return; }
+ size_t size() {
+ return string_ref_list_init ? string_ref_list.elements : 0; }
+ bool resize(size_t new_size) {
+ if (string_ref_list_init) {
+ if (string_ref_list.max_element < new_size && allocate_dynamic(
+ &string_ref_list, new_size)) return TRUE;
+ string_ref_list.elements = new_size;
+ return FALSE;
+ }
+ return TRUE;
+ }
+ bool empty() {
+ return string_ref_list_init ? string_ref_list.elements ?
+ FALSE : TRUE : TRUE; }
+ string_ref &operator [](size_t n) {
+ return ((string_ref *) (string_ref_list.buffer +
+ string_ref_list.size_of_element * n))[0]; }
+ bool string_ref_list_init;
+ DYNAMIC_ARRAY string_ref_list;
+};
+
+inline String *
+q_append_str(String *str, const char *p) {
+ uint32 p_len = strlen(p);
+ if (str->reserve(p_len)) return NULL;
+ str->q_append(p, p_len); return str;
+}
+
+};
+
+#endif
+
diff --git a/storage/spider/hs_client/string_util.cpp b/storage/spider/hs_client/string_util.cpp
new file mode 100644
index 00000000..39934148
--- /dev/null
+++ b/storage/spider/hs_client/string_util.cpp
@@ -0,0 +1,207 @@
+
+// vim:sw=2:ai
+
+/*
+ * Copyright (C) 2010-2011 DeNA Co.,Ltd.. All rights reserved.
+ * Copyright (C) 2011-2017 Kentoku SHIBA
+ * See COPYRIGHT.txt for details.
+ */
+
+#include <my_global.h>
+#include "mysql_version.h"
+#include "hs_compat.h"
+#if MYSQL_VERSION_ID < 50500
+#include "mysql_priv.h"
+#include <mysql/plugin.h>
+#else
+#include "sql_priv.h"
+#include "probes_mysql.h"
+#endif
+
+#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);
+}
+
+uint32
+atoi_uint32_nocheck(const char *start, const char *finish)
+{
+ uint32 v = 0;
+ for (; start != finish; ++start) {
+ const char c = *start;
+ if (c < '0' || c > '9') {
+ break;
+ }
+ v *= 10;
+ v += (uint32) (c - '0');
+ }
+ return v;
+}
+
+long long
+atoll_nocheck(const char *start, const char *finish)
+{
+ long long 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 -= (long long) (c - '0');
+ } else {
+ v += (long long) (c - '0');
+ }
+ }
+ return v;
+}
+
+void
+append_uint32(string_buffer& buf, uint32 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);
+ }
+}
+
+/*
+String *
+to_stdstring(uint32 v)
+{
+ char buf[64];
+ int str_len;
+ String *str;
+ str_len = snprintf(buf, sizeof(buf), "%lu", static_cast<unsigned long>(v));
+ if ((str = new String(str_len + 1)))
+ str->q_append(buf, str_len);
+ return str;
+}
+*/
+
+int
+errno_string(const char *s, int en, String& err_r)
+{
+ char buf[64];
+ int str_len;
+ str_len = snprintf(buf, sizeof(buf), "%s: %d", s, en);
+ if (!err_r.reserve(str_len + 1))
+ err_r.q_append(buf, str_len);
+ return en;
+}
+
+size_t
+split(char delim, const string_ref& buf, string_ref *parts,
+ size_t parts_len)
+{
+ size_t i = 0;
+ const char *start = buf.begin();
+ const char *const finish = buf.end();
+ for (i = 0; i < parts_len; ++i) {
+ const char *const p = memchr_char(start, delim, finish - start);
+ if (p == 0) {
+ parts[i] = string_ref(start, finish - start);
+ ++i;
+ break;
+ }
+ parts[i] = string_ref(start, p - start);
+ start = p + 1;
+ }
+ const size_t r = i;
+ for (; i < parts_len; ++i) {
+ parts[i] = string_ref();
+ }
+ return r;
+}
+
+size_t
+split(char delim, const string_wref& buf, string_wref *parts,
+ size_t parts_len)
+{
+ size_t i = 0;
+ char *start = buf.begin();
+ char *const finish = buf.end();
+ for (i = 0; i < parts_len; ++i) {
+ char *const p = memchr_char(start, delim, finish - start);
+ if (p == 0) {
+ parts[i] = string_wref(start, finish - start);
+ ++i;
+ break;
+ }
+ parts[i] = string_wref(start, p - start);
+ start = p + 1;
+ }
+ const size_t r = i;
+ for (; i < parts_len; ++i) {
+ parts[i] = string_wref();
+ }
+ return r;
+}
+
+size_t
+split(char delim, const string_ref& buf, DYNAMIC_ARRAY& parts_r)
+{
+ size_t i = 0;
+ const char *start = buf.begin();
+ const char *finish = buf.end();
+ while (true) {
+ const char *p = memchr_char(start, delim, finish - start);
+ if (p == 0) {
+ string_ref param(start, finish - start);
+ insert_dynamic(&parts_r, (uchar *) &param);
+ break;
+ }
+ string_ref param(start, p - start);
+ insert_dynamic(&parts_r, (uchar *) &param);
+ start = p + 1;
+ }
+ const size_t r = i;
+ return r;
+}
+
+size_t
+split(char delim, const string_wref& buf, DYNAMIC_ARRAY& parts_r)
+{
+ size_t i = 0;
+ char *start = buf.begin();
+ char *finish = buf.end();
+ while (true) {
+ char *p = memchr_char(start, delim, finish - start);
+ if (p == 0) {
+ string_wref param(start, finish - start);
+ insert_dynamic(&parts_r, (uchar *) &param);
+ break;
+ }
+ string_wref param(start, p - start);
+ insert_dynamic(&parts_r, (uchar *) &param);
+ start = p + 1;
+ }
+ const size_t r = i;
+ return r;
+}
+
+};
+
diff --git a/storage/spider/hs_client/string_util.hpp b/storage/spider/hs_client/string_util.hpp
new file mode 100644
index 00000000..b886adde
--- /dev/null
+++ b/storage/spider/hs_client/string_util.hpp
@@ -0,0 +1,51 @@
+
+// vim:sw=2:ai
+
+/*
+ * Copyright (C) 2010-2011 DeNA Co.,Ltd.. All rights reserved.
+ * Copyright (C) 2011 Kentoku SHIBA
+ * See COPYRIGHT.txt for details.
+ */
+
+#ifndef DENA_STRING_UTIL_HPP
+#define DENA_STRING_UTIL_HPP
+
+#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 atoi_uint32_nocheck(const char *start, const char *finish);
+/*
+String *to_stdstring(uint32 v);
+*/
+void append_uint32(string_buffer& buf, uint32 v);
+long long atoll_nocheck(const char *start, const char *finish);
+
+int errno_string(const char *s, int en, 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,
+ DYNAMIC_ARRAY& parts_r);
+size_t split(char delim, const string_wref& buf,
+ DYNAMIC_ARRAY& parts_r);
+};
+
+#endif
+
diff --git a/storage/spider/hs_client/thread.hpp b/storage/spider/hs_client/thread.hpp
new file mode 100644
index 00000000..79fba706
--- /dev/null
+++ b/storage/spider/hs_client/thread.hpp
@@ -0,0 +1,84 @@
+
+// vim:sw=2:ai
+
+/*
+ * Copyright (C) 2010-2011 DeNA Co.,Ltd.. All rights reserved.
+ * Copyright (C) 2011 Kentoku SHIBA
+ * See COPYRIGHT.txt for details.
+ */
+
+#ifndef DENA_THREAD_HPP
+#define DENA_THREAD_HPP
+
+#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;
+ }
+ 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;
+ }
+ need_join = true;
+ return need_join;
+ }
+ void join() {
+ if (!need_join) {
+ return;
+ }
+ int e = 0;
+ if ((e = pthread_join(thr, 0)) != 0) {
+ fatal_abort("pthread_join");
+ }
+ need_join = false;
+ }
+ T& operator *() { return obj; }
+ T *operator ->() { return &obj; }
+ private:
+ static void *thread_main(void *arg) {
+ thread *p = static_cast<thread *>(arg);
+ p->obj();
+ return 0;
+ }
+ private:
+ T obj;
+ pthread_t thr;
+ bool need_join;
+ size_t stack_size;
+};
+*/
+
+};
+
+#endif
+
diff --git a/storage/spider/hs_client/util.hpp b/storage/spider/hs_client/util.hpp
new file mode 100644
index 00000000..93d78cc7
--- /dev/null
+++ b/storage/spider/hs_client/util.hpp
@@ -0,0 +1,25 @@
+
+// vim:sw=2:ai
+
+/*
+ * Copyright (C) 2010 DeNA Co.,Ltd.. All rights reserved.
+ * See COPYRIGHT.txt for details.
+ */
+
+#ifndef DENA_UTIL_HPP
+#define DENA_UTIL_HPP
+
+namespace dena {
+
+/* boost::noncopyable */
+struct noncopyable {
+ noncopyable() { }
+ private:
+ noncopyable(const noncopyable&);
+ noncopyable& operator =(const noncopyable&);
+};
+
+};
+
+#endif
+