diff --git a/.travis.yml b/.travis.yml index 01a081a5ffb3a905baf0d54a187e6a2022c7ebad..b19d7775abf189261057cb0a329c58542523c86c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -24,3 +24,6 @@ notifications: on_success: change on_failure: always email: false + +git: + depth: 500 diff --git a/client/tarantool/CMakeLists.txt b/client/tarantool/CMakeLists.txt index c8f1a369ac60e870e3beb312771fadf09b82c69c..d03e72375cf7c3bd7f1b30a3afabb4646499cf6b 100644 --- a/client/tarantool/CMakeLists.txt +++ b/client/tarantool/CMakeLists.txt @@ -8,7 +8,8 @@ if (NOT READLINE_FOUND) endif() set (cli "tarantool") -set (cli_sources tc.c tc_opt.c tc_admin.c tc_query.c tc_print.c tc_cli.c tc_store.c) +set (cli_sources tc.c tc_opt.c tc_admin.c tc_query.c tc_print.c tc_buf.c + tc_cli.c tc_store.c tc_print_xlog.c tc_print_snap.c) set (cli_libs tntrpl tntnet tntsql tnt gopt ${READLINE_LIBRARIES}) include_directories(${READLINE_INCLUDE_DIR}) diff --git a/client/tarantool/tc.c b/client/tarantool/tc.c index ffb96d5c3c7a94e15f3c12dee1c4cc78cf950226..2b190f07b291361f9812fc28099222e376a68173 100644 --- a/client/tarantool/tc.c +++ b/client/tarantool/tc.c @@ -45,6 +45,8 @@ #include "client/tarantool/tc.h" #include "client/tarantool/tc_cli.h" #include "client/tarantool/tc_print.h" +#include "client/tarantool/tc_print_snap.h" +#include "client/tarantool/tc_print_xlog.h" #include "client/tarantool/tc_store.h" struct tc tc; @@ -103,10 +105,15 @@ static void tc_connect_admin(void) static void tc_validate(void) { - tc.opt.printer = tc_print_getcb(tc.opt.format); - if (tc.opt.printer == NULL) - return tc_error("unsupported output format '%s'", + tc.opt.xlog_printer = tc_print_getxlogcb(tc.opt.format); + tc.opt.snap_printer = tc_print_getsnapcb(tc.opt.format); + if (tc.opt.xlog_printer == NULL) + return tc_error("unsupported output xlog format '%s'", tc.opt.format); + if (tc.opt.snap_printer == NULL) + return tc_error("unsupported output snap format '%s'", + tc.opt.format); + if (tc.opt.format && strcmp(tc.opt.format, "raw") == 0) tc.opt.raw = 1; } diff --git a/client/tarantool/tc_admin.c b/client/tarantool/tc_admin.c index bdd4a806837c5dd05fbe2f6e2d92e738ac1084a9..7c941ee49c8083d19cce300cecae2d3740b0e031 100644 --- a/client/tarantool/tc_admin.c +++ b/client/tarantool/tc_admin.c @@ -115,9 +115,11 @@ int tc_admin_reply(struct tc_admin *a, char **r, size_t *size) ssize_t rxi = recv(a->fd, rx, sizeof(rx), 0); if (rxi <= 0) break; - char *bufn = realloc(buf, off + rxi + 1); - if (bufn == NULL) + char *bufn = (char *)realloc(buf, off + rxi + 1); + if (bufn == NULL) { + free(buf); break; + } buf = bufn; memcpy(buf + off, rx, rxi); off += rxi; diff --git a/client/tarantool/tc_buf.c b/client/tarantool/tc_buf.c new file mode 100644 index 0000000000000000000000000000000000000000..b4556bb796078d1b86f5b54a84b2146268e22c87 --- /dev/null +++ b/client/tarantool/tc_buf.c @@ -0,0 +1,126 @@ +/* + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``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 + * <COPYRIGHT HOLDER> OR CONTRIBUTORS 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. + */ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stddef.h> +#include <ctype.h> + +#include "client/tarantool/tc_buf.h" + +/* Strip trailing ws from (char*) */ +size_t strip_end_ws(char *str) { + size_t last = 0; + for (size_t i = 0; str[i] != 0; ++i) + if (!isspace(str[i])) + last = i + 1; + str[last] = '\0'; + return last; +} + +/* Init membuf */ +int tc_buf(struct tc_buf *buf) { + buf->size = TC_BUF_INIT_SIZE; + buf->used = 0; + buf->data = (char *)malloc(buf->size); + if (buf->data == NULL) + return -1; + return 0; +} + +/* Append len bytes of memory from str pointed memory */ +int tc_buf_append(struct tc_buf *buf, void *str, size_t len) { + if (buf->size - buf->used < len) { + if (buf->size < len) + buf->size = len; + buf->size *= TC_BUF_MULTIPLIER; + char *nd = (char *)realloc(buf->data, buf->size); + if (nd == NULL) + return -1; + buf->data = nd; + } + memcpy(buf->data + buf->used, str, len); + buf->used += len; + return 0; +} + +/* Remove last "num" symbols */ +size_t tc_buf_delete(struct tc_buf *buf, size_t num) { + if (buf->used > num) { + buf->used -= num; + } else { + num = buf->used; + buf->used = 0; + } + return num; +} + +inline int tc_buf_isempty(struct tc_buf *buf) { + return (buf->used == 0); +} + +inline void tc_buf_clear(struct tc_buf *buf) { + buf->used = 0; +} + +/* Free membuffer */ +void tc_buf_free(struct tc_buf *buf) { + if (buf->data) + free(buf->data); +} + +/* Init buffer as STR */ +int tc_buf_str(struct tc_buf *buf) { + if (tc_buf(buf)) + return -1; + return tc_buf_append(buf, (void *)"\0", 1); +} + +/* Append str to STR */ +int tc_buf_str_append(struct tc_buf *buf, char *str, size_t len) { + tc_buf_delete(buf, 1); + if (tc_buf_append(buf, (void *)str, len)) + return -1; + if (tc_buf_append(buf, (void *)"\0", 1)) + return -1; + return 0; +} + +/* Remove trailing ws from STR */ +int tc_buf_str_stripws(struct tc_buf *buf) { + if (buf->data) { + buf->used = 1 + strip_end_ws(buf->data); + return 0; + } + return -1; +} + +inline int tc_buf_str_isempty(struct tc_buf *buf) { + return (buf->used == 1 ? 1 : 0) || (tc_buf_isempty(buf)); +} diff --git a/client/tarantool/tc_buf.h b/client/tarantool/tc_buf.h new file mode 100644 index 0000000000000000000000000000000000000000..52383666e7a7adf1cd869c1e96ff3a4e197d781d --- /dev/null +++ b/client/tarantool/tc_buf.h @@ -0,0 +1,51 @@ +/* + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``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 + * <COPYRIGHT HOLDER> OR CONTRIBUTORS 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. + */ +#define TC_BUF_INIT_SIZE 4096 +#define TC_BUF_MULTIPLIER 2 + +size_t strip_end_ws(char *str); + +struct tc_buf { + size_t size; + size_t used; + char *data; +}; + +int tc_buf(struct tc_buf *buf); +void *tc_buf_realloc(void *data, size_t size); +int tc_buf_append(struct tc_buf *buf, void *str, size_t len); +size_t tc_buf_delete(struct tc_buf *buf, size_t len); +int tc_buf_isempty(struct tc_buf *buf); +void tc_buf_clear(struct tc_buf *buf); +void tc_buf_free(struct tc_buf *buf); + +int tc_buf_str(struct tc_buf *buf); +int tc_buf_str_append(struct tc_buf *buf, char *str, size_t len); +int tc_buf_str_stripws(struct tc_buf *buf); +int tc_buf_str_isempty(struct tc_buf *buf); diff --git a/client/tarantool/tc_cli.c b/client/tarantool/tc_cli.c index efe47330fb0f64b764fd38665ddb6edd2cf9d8dc..b7255ef4c6c195c69f8f8cbb917da4ab96a1ebff 100644 --- a/client/tarantool/tc_cli.c +++ b/client/tarantool/tc_cli.c @@ -32,6 +32,7 @@ #include <stdarg.h> #include <string.h> #include <stdint.h> +#include <ctype.h> #include <sys/types.h> #include <sys/stat.h> @@ -39,6 +40,7 @@ #include <fcntl.h> #include <signal.h> #include <errno.h> +#include <wchar.h> #include <readline/readline.h> #include <readline/history.h> @@ -58,9 +60,13 @@ #include "client/tarantool/tc_query.h" #include "client/tarantool/tc_cli.h" #include "client/tarantool/tc_print.h" +#include "client/tarantool/tc_buf.h" #define TC_DEFAULT_HISTORY_FILE ".tarantool_history" +#define TC_ALLOCATION_ERROR "error: memory allocation failed for %zu bytes\n" +#define TC_REALLOCATION_ERROR "error: memory reallocation failed for %zu bytes\n" + extern struct tc tc; static inline int tc_cli_error(char *e) { @@ -89,7 +95,9 @@ enum tc_keywords { TC_TEE, TC_NOTEE, TC_LOADFILE, - TC_HELP + TC_HELP, + TC_SETOPT, + TC_SETOPT_DELIM }; static struct tnt_lex_keyword tc_lex_keywords[] = @@ -106,6 +114,10 @@ static struct tnt_lex_keyword tc_lex_keywords[] = { "tee", 3, TC_TEE }, { "notee", 5, TC_NOTEE }, { "loadfile", 8, TC_LOADFILE }, + { "s", 1, TC_SETOPT}, + { "setopt", 6, TC_SETOPT}, + { "delim", 5, TC_SETOPT_DELIM}, + { "delimiter", 9, TC_SETOPT_DELIM}, { NULL, 0, TNT_TK_NONE } }; @@ -125,6 +137,8 @@ tc_cmd_usage(void) " - tee 'path'\n" " - notee\n" " - loadfile 'path'\n" + " - setopt key=val\n" + " - (possible pairs: delim=\'str\')\n" "...\n"; tc_printf("%s", usage); } @@ -194,9 +208,9 @@ tc_cmd_loadfile(char *path, int *reconnect) int fd = open(path, O_RDONLY); if (fd == -1) return -1; - char *buf = malloc(st.st_size); + char *buf = (char *)malloc(st.st_size); if (buf == NULL) { - tc_printf("error: memory allocation failed for %zu bytes\n", + tc_printf(TC_ALLOCATION_ERROR, st.st_size); return -1; } @@ -252,6 +266,37 @@ tc_cmd_try(char *cmd, size_t size, int *reconnect) if (tc_cmd_loadfile((char*)TNT_TK_S(tk)->data, reconnect) == -1) rc = TC_CLI_ERROR; goto done; + case TC_SETOPT: + switch (tnt_lex(&lex, &tk)) { + case TC_SETOPT_DELIM: + if (tnt_lex(&lex, &tk) == '=' && + tnt_lex(&lex, &tk) == TNT_TK_STRING) { + if (!TNT_TK_S(tk)->size) { + tc.opt.delim = ""; + tc.opt.delim_len = 0; + goto done; + } + char * temp = (char *)malloc(TNT_TK_S(tk)->size); + if (temp == NULL) + tc_error(TC_ALLOCATION_ERROR, + TNT_TK_S(tk)->size); + strncpy(temp, + (const char *)TNT_TK_S(tk)->data, + TNT_TK_S(tk)->size + 1); + tc.opt.delim = temp; + tc.opt.delim_len = strlen(tc.opt.delim); + } else { + tc_printf("---\n"); + tc_printf(" - Expected delim='string'\n"); + tc_printf("---\n"); + } + break; + default: + tc_printf("---\n"); + tc_printf(" - Unknown option to set\n"); + tc_printf("---\n"); + } + goto done; } *reconnect = tc_cli_admin(cmd, rc == TC_CLI_EXIT); if (*reconnect) @@ -321,6 +366,56 @@ static void tc_cli_init(void) { tc_error("signal initialization failed\n"); } +static char* tc_cli_readline_pipe() { + int size = 8192, pos = 0; + const size_t wcsize = sizeof(wchar_t); + char *str = (char *)malloc(size); + if (str == NULL) + tc_error(TC_ALLOCATION_ERROR, size); + wchar_t c; + while ((c = getwchar())) { + if (size < (pos + wcsize)) { + size *= 2; + char *nd = (char *)realloc(str, size); + if (nd == NULL) + tc_error(TC_REALLOCATION_ERROR, size); + str = nd; + } + if (c == '\r' || c == '\n' || c == WEOF) { + char c_t = (c != WEOF ? getchar() : '\n'); + if (c_t != '\r' && c_t != '\n') + ungetc(c_t, stdin); + wctomb(str + pos++, 0); + break; + } + else + pos += wctomb(str + pos, c); + } + if (pos == 1 && c == WEOF) { + free(str); + return NULL; + } + return str; +} + + + +static int check_delim(char* str, size_t len, size_t sep_len) { + const char *sep = tc.opt.delim; + len = strip_end_ws(str); + if (sep_len == 0) + return 1; + if (len < sep_len) + return 0; + size_t i; + for (i = len; sep_len > 0; --sep_len, --i) + if (str[i - 1] != sep[sep_len - 1]) + return 0; + str[i] = '\0'; + len = strip_end_ws(str); + return 1; +} + int tc_cli(void) { /* initializing cli */ @@ -335,25 +430,62 @@ int tc_cli(void) /* setting prompt */ char prompt[128]; - snprintf(prompt, sizeof(prompt), "%s> ", tc.opt.host); - + int prompt_len = snprintf(prompt, sizeof(prompt), "%s> ", tc.opt.host) - 2; + char prompt_delim[128]; /* interactive mode */ - char *cmd; - while ((cmd = readline(prompt))) { - if (!cmd[0]) - goto next; - int cmd_len = strlen(cmd); - tc_print_cmd2tee(prompt, cmd, cmd_len); - enum tc_cli_cmd_ret ret = tc_cli_cmd(cmd, cmd_len); - if (ret == TC_CLI_EXIT) + char *part_cmd; + struct tc_buf cmd; + if (tc_buf_str(&cmd)) + tc_error(TC_REALLOCATION_ERROR, + cmd.size); + while (1) { + if (isatty(STDIN_FILENO)) { + snprintf(prompt_delim, sizeof(prompt_delim), + "%*s> ", prompt_len, "-"); + part_cmd = readline(!tc_buf_str_isempty(&cmd) ? prompt_delim + : prompt); + } else { + clearerr(stdin); + part_cmd = tc_cli_readline_pipe(); + } + if (!part_cmd) break; - add_history(cmd); + size_t part_cmd_len = strlen(part_cmd); + int delim_exists = check_delim(part_cmd, + part_cmd_len, + tc.opt.delim_len); + if (tc_buf_str_append(&cmd, part_cmd, strlen(part_cmd))) + tc_error(TC_REALLOCATION_ERROR, + cmd.size); + free(part_cmd); + if (!delim_exists && !feof(stdin)) { + if (tc_buf_str_append(&cmd, " ", 1)) + tc_error(TC_REALLOCATION_ERROR, + cmd.size); + continue; + } + tc_buf_str_stripws(&cmd); + if (delim_exists && tc_buf_str_isempty(&cmd)) + goto next; + tc_print_cmd2tee(cmd.used != 1 ? prompt_delim : prompt, + cmd.data, cmd.used - 1); + enum tc_cli_cmd_ret ret = tc_cli_cmd(cmd.data, + cmd.used - 1); + if (isatty(STDIN_FILENO)) + add_history(cmd.data); next: - free(cmd); - } + tc_buf_clear(&cmd); + if (ret == TC_CLI_EXIT || feof(stdin)) { + tc_buf_free(&cmd); + break; + } +} /* updating history file */ write_history(history); clear_history(); return 0; } + +#undef TC_ALLOCATION_ERROR +#undef TC_REALLOCATION_ERROR diff --git a/client/tarantool/tc_opt.c b/client/tarantool/tc_opt.c index c666b292110c7216f1539657afc8e71f9d8d10d0..331ad3e59a836853b98be0064a95b3f2ab7a94bf 100644 --- a/client/tarantool/tc_opt.c +++ b/client/tarantool/tc_opt.c @@ -35,6 +35,7 @@ #include "client/tarantool/tc_opt.h" + #define TC_DEFAULT_HOST "localhost" #define TC_DEFAULT_PORT 33013 #define TC_DEFAULT_PORT_ADMIN 33015 @@ -63,6 +64,14 @@ static const void *tc_options_def = gopt_start( gopt_longs("header"), NULL, "add file headers for the raw output"), gopt_option('R', GOPT_ARG, gopt_shorts('R'), gopt_longs("rpl"), " <lsn>", "act as replica for the specified server"), + gopt_option('B', 0, gopt_shorts('B'), + gopt_longs("bin"), NULL, "print STR in lua printer instead" + " of NUM32 and NUM64, except arithmetic update arguments"), + gopt_option('D', GOPT_ARG, gopt_shorts('D'), + gopt_longs("delim"), " <delim>", + "if you use --cat, then it will add delim to an end of every line of your" + "lua file, when used at CLI start of client, then it's replacement of " + "setopt delim='<delim>' command"), gopt_option('?', 0, gopt_shorts(0), gopt_longs("help"), NULL, "display this help and exit"), gopt_option('v', 0, gopt_shorts('v'), gopt_longs("version"), @@ -148,6 +157,17 @@ enum tc_opt_mode tc_opt_init(struct tc_opt *opt, int argc, char **argv) if (gopt(tc_options, 'H')) opt->raw_with_headers = 1; + /* string instead of num and num64 */ + opt->str_instead_int = 0; + if (gopt(tc_options, 'B')) + opt->str_instead_int = 1; + + /* set delimiter on start */ + opt->delim = ""; + opt->delim_len = 0; + if (gopt_arg(tc_options, 'D', &opt->delim)) + opt->delim_len = strlen(opt->delim); + /* replica mode */ if (gopt_arg(tc_options, 'R', &arg)) { opt->mode = TC_OPT_RPL; diff --git a/client/tarantool/tc_opt.h b/client/tarantool/tc_opt.h index 931e95150fcca5db80b95f7be830216aa784a41c..cef473890b3b1009f4ae54e671bd917537448699 100644 --- a/client/tarantool/tc_opt.h +++ b/client/tarantool/tc_opt.h @@ -57,10 +57,14 @@ struct tc_opt { const char *format; int raw; int raw_with_headers; - void *printer; + int str_instead_int; + void *xlog_printer; + void *snap_printer; const char *file; char **cmdv; int cmdc; + const char *delim; + size_t delim_len; }; void tc_opt_usage(void); diff --git a/client/tarantool/tc_print.c b/client/tarantool/tc_print.c index 78f64dce43d529773c431ff0c9dbfcffc6546a95..bacdef37eb3d390221cb1e31668df138447d8b0d 100644 --- a/client/tarantool/tc_print.c +++ b/client/tarantool/tc_print.c @@ -37,6 +37,7 @@ #include <unistd.h> #include <errno.h> + #include <connector/c/include/tarantool/tnt.h> #include <connector/c/include/tarantool/tnt_xlog.h> #include <connector/c/include/tarantool/tnt_rpl.h> @@ -49,6 +50,8 @@ extern struct tc tc; +/*##################### Base printing functions #####################*/ + void tc_print_tee(char *buf, size_t size) { if (tc.tee_fd == -1) return; @@ -95,6 +98,8 @@ void tc_printf(char *fmt, ...) { } } +/*##################### string functions #####################*/ + static int tc_str_valid(char *data, uint32_t size) { int length; wchar_t dest; @@ -111,19 +116,34 @@ static int tc_str_valid(char *data, uint32_t size) { return 1; } -static void tc_print_str(char *data, uint32_t size) { +void tc_print_string(char *data, uint32_t size, char lua) +{ if (tc_str_valid(data, size)) { wchar_t dest; int length; mbtowc (NULL, NULL, 0); while ((length = mbtowc(&dest, data, size)) > -1 && size > 0) { if (dest >= 0x20) { - tc_printf ("%lc", dest); + if (lua) + switch (dest) { + case '\'': + tc_printf("\\\'"); + break; + case '\\': + tc_printf("\\\\"); + break; + default: + tc_printf ("%lc", dest); + } + else + tc_printf ("%lc", dest); } else { switch (dest) { case 0x00: tc_printf("\\0"); + length++; + /* Cause of mbtowc returns 0 when \0 */ break; case 0x07: tc_printf("\\a"); @@ -147,12 +167,10 @@ static void tc_print_str(char *data, uint32_t size) { tc_printf("\\r"); break; default: - tc_printf("\\x%02lX", + tc_printf("\\x%02lX", (unsigned long int)dest); break; } - if (length == 0) - ++length; } size -= length; data += length; @@ -166,7 +184,11 @@ static void tc_print_str(char *data, uint32_t size) { } } -static void tc_print_fields(struct tnt_tuple *tu) { +/*##################### Tuple and Fields #####################*/ +/* tarantool */ + +void tc_print_fields(struct tnt_tuple *tu) +{ struct tnt_iter ifl; tnt_iter(&ifl, tu); while (tnt_next(&ifl)) { @@ -183,7 +205,7 @@ static void tc_print_fields(struct tnt_tuple *tu) { tc_printf("%"PRIu64, *((uint64_t*)data)); break; default: - tc_print_str(data, size); + tc_print_string(data, size, 0); } tc_printf("'"); } @@ -210,46 +232,46 @@ void tc_print_list(struct tnt_list *l) tnt_iter_free(&it); } -static void -tc_printer_tarantool(struct tnt_log_header_v11 *hdr, - struct tnt_request *r) +/* lua */ + +void tc_print_lua_field(char *data, uint32_t size, char string) { - tc_printf("%s lsn: %"PRIu64", time: %f, len: %"PRIu32"\n", - tc_query_type(r->h.type), - hdr->lsn, - hdr->tm, - hdr->len); - switch (r->h.type) { - case TNT_OP_INSERT: - tc_print_tuple(&r->r.insert.t); + if (string) + goto _string; + switch (size){ + case 4: + tc_printf("%"PRIu32, *((uint32_t*)data)); break; - case TNT_OP_DELETE: - tc_print_tuple(&r->r.del.t); - break; - case TNT_OP_UPDATE: - tc_print_tuple(&r->r.update.t); + case 8: + tc_printf("%"PRIu64, *((uint64_t*)data)); break; + default: +_string: + tc_printf("\'"); + tc_print_string(data, size, 1); + tc_printf("\'"); } } -static void -tc_printer_raw(struct tnt_log_header_v11 *hdr, struct tnt_request *r) +void tc_print_lua_fields(struct tnt_tuple *tu) { - if (tc.opt.raw_with_headers) { - fwrite(&tnt_log_marker_v11, - sizeof(tnt_log_marker_v11), 1, stdout); + struct tnt_iter ifl; + tnt_iter(&ifl, tu); + while (tnt_next(&ifl)) { + if ((TNT_IFIELD_IDX(&ifl)) != 0) + tc_printf(", "); + char *data = TNT_IFIELD_DATA(&ifl); + uint32_t size = TNT_IFIELD_SIZE(&ifl); + tc_print_lua_field(data, size, tc.opt.str_instead_int); } - fwrite(hdr, sizeof(*hdr), 1, stdout); - fwrite(r->origin, r->origin_size, 1, stdout); + if (ifl.status == TNT_ITER_FAIL) + tc_printf("<parsing error>"); + tnt_iter_free(&ifl); } -tc_printerf_t tc_print_getcb(const char *name) +void tc_print_lua_tuple(struct tnt_tuple *tu) { - if (name == NULL) - return tc_printer_tarantool; - if (!strcasecmp(name, "tarantool")) - return tc_printer_tarantool; - if (!strcasecmp(name, "raw")) - return tc_printer_raw; - return NULL; + tc_printf("{"); + tc_print_lua_fields(tu); + tc_printf("}"); } diff --git a/client/tarantool/tc_print.h b/client/tarantool/tc_print.h index 4af4b1b87d3104fc0d56cce61f48bde9c269096a..fe2612e74b113fdae5b1ccdceb2fca883c91d63f 100644 --- a/client/tarantool/tc_print.h +++ b/client/tarantool/tc_print.h @@ -29,17 +29,19 @@ * SUCH DAMAGE. */ -typedef void (*tc_printerf_t)(struct tnt_log_header_v11 *hdr, - struct tnt_request *r); - void tc_print_tee(char *buf, size_t size); void tc_print_cmd2tee(char *prompt, char *cmd, int size); void tc_printf(char *fmt, ...); void tc_print_buf(char *buf, size_t size); +void tc_print_string(char *data, uint32_t size, char lua); + +void tc_print_fields(struct tnt_tuple *tu); void tc_print_tuple(struct tnt_tuple *tu); void tc_print_list(struct tnt_list *l); -tc_printerf_t tc_print_getcb(const char *name); +void tc_print_lua_field(char *data, uint32_t size, char string); +void tc_print_lua_fields(struct tnt_tuple *tu); +void tc_print_lua_tuple(struct tnt_tuple *tu); #endif /* TC_PRINT_H_INCLUDED */ diff --git a/client/tarantool/tc_print_snap.c b/client/tarantool/tc_print_snap.c new file mode 100644 index 0000000000000000000000000000000000000000..e08d183994722288657c9d7687efafa98f7cbadb --- /dev/null +++ b/client/tarantool/tc_print_snap.c @@ -0,0 +1,68 @@ +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <inttypes.h> +#include <limits.h> +#include <stdint.h> + +#include <connector/c/include/tarantool/tnt.h> +#include <connector/c/include/tarantool/tnt_xlog.h> +#include <connector/c/include/tarantool/tnt_snapshot.h> + +#include "client/tarantool/tc_opt.h" +#include "client/tarantool/tc_admin.h" +#include "client/tarantool/tc.h" +#include "client/tarantool/tc_print.h" +#include "client/tarantool/tc_print_snap.h" +#include "client/tarantool/tc_query.h" +#include "client/tarantool/tc_store.h" + +extern struct tc tc; + +static void +tc_printer_snap_raw( struct tnt_log_row_snap_v11 *row, + struct tnt_tuple *tu) +{ + if (tc.opt.raw_with_headers) { + fwrite(&tnt_log_marker_v11, + sizeof(tnt_log_marker_v11), 1, stdout); + } + fwrite(row, sizeof(row), 1, stdout); + fwrite(tu->data, tu->size, 1, stdout); +} +static void +tc_printer_snap_tarantool( struct tnt_log_row_snap_v11 *row, + struct tnt_tuple *tu) +{ + tc_printf("tag: %"PRIu16", cookie: %"PRIu64", space: %"PRIu32"\n", + row->tag, + row->cookie, + row->space); + tc_print_tuple(tu); + +} +static void +tc_printer_snap_lua( struct tnt_log_row_snap_v11 *row, + struct tnt_tuple *tu) +{ + tc_printf("lua box.insert(%"PRIu32", ", row->space); + tc_print_lua_fields(tu); + tc_printf(")"); + if (tc.opt.delim_len > 0) + tc_printf("%s\n", tc.opt.delim); + else + tc_printf("\n"); +} + +tc_printerf_snap_t tc_print_getsnapcb(const char *name) +{ + if (name == NULL) + return tc_printer_snap_tarantool; + if (!strcasecmp(name, "tarantool")) + return tc_printer_snap_tarantool; + if (!strcasecmp(name, "raw")) + return tc_printer_snap_raw; + if (!strcasecmp(name, "lua")) + return tc_printer_snap_lua; + return NULL; +} diff --git a/client/tarantool/tc_print_snap.h b/client/tarantool/tc_print_snap.h new file mode 100644 index 0000000000000000000000000000000000000000..9bf467a2bc062be0b8e9c8cfe55dec5bc5d99250 --- /dev/null +++ b/client/tarantool/tc_print_snap.h @@ -0,0 +1,37 @@ +#ifndef TC_PRINT_SNAP_H_INCLUDED +#define TC_PRINT_SNAP_H_INCLUDED +/* + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``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 + * <COPYRIGHT HOLDER> OR CONTRIBUTORS 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. + */ + +typedef void (*tc_printerf_snap_t)(struct tnt_log_row_snap_v11 *row, + struct tnt_tuple *tu); + +tc_printerf_snap_t tc_print_getsnapcb(const char *name); + +#endif /* TC_PRINT_SNAP_H_INCLUDED */ diff --git a/client/tarantool/tc_print_xlog.c b/client/tarantool/tc_print_xlog.c new file mode 100644 index 0000000000000000000000000000000000000000..57f48513cd957f02eea149a2e3b01d3865c573b3 --- /dev/null +++ b/client/tarantool/tc_print_xlog.c @@ -0,0 +1,197 @@ +/* + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``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 + * <COPYRIGHT HOLDER> OR CONTRIBUTORS 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. + */ + +#include <stddef.h> +#include <stdlib.h> +#include <stdio.h> +#include <inttypes.h> +#include <strings.h> + +#include <connector/c/include/tarantool/tnt.h> +#include <connector/c/include/tarantool/tnt_xlog.h> +#include <connector/c/include/tarantool/tnt_rpl.h> + +#include "client/tarantool/tc_opt.h" +#include "client/tarantool/tc_admin.h" +#include "client/tarantool/tc_print.h" +#include "client/tarantool/tc_print_xlog.h" +#include "client/tarantool/tc_query.h" +#include "client/tarantool/tc_print.h" +#include "client/tarantool/tc.h" + +extern struct tc tc; + +static void +tc_printer_xlog_raw(struct tnt_log_header_v11 *hdr, + struct tnt_request *r) +{ + if (tc.opt.raw_with_headers) { + fwrite(&tnt_log_marker_v11, + sizeof(tnt_log_marker_v11), 1, stdout); + } + fwrite(hdr, sizeof(*hdr), 1, stdout); + fwrite(r->origin, r->origin_size, 1, stdout); +} + +static void +tc_printer_xlog_tarantool(struct tnt_log_header_v11 *hdr, + struct tnt_request *r) +{ + tc_printf("%s lsn: %"PRIu64", time: %f, len: %"PRIu32"\n", + tc_query_type(r->h.type), + hdr->lsn, + hdr->tm, + hdr->len); + switch (r->h.type) { + case TNT_OP_INSERT: + tc_print_tuple(&r->r.insert.t); + break; + case TNT_OP_DELETE: + tc_print_tuple(&r->r.del.t); + break; + case TNT_OP_UPDATE: + tc_print_tuple(&r->r.update.t); + break; + } +} + +static void +tc_printer_xlog_lua(struct tnt_log_header_v11 *hdr, + struct tnt_request *r) +{ + tc_printf("lua box."); + switch (r->h.type) { + case TNT_OP_INSERT: + if (r->r.insert.h.flags && TNT_FLAG_REPLACE == TNT_FLAG_REPLACE) + tc_printf("replace("); + else + tc_printf("insert("); + tc_printf("%"PRIu32", ", r->r.insert.h.ns); + tc_print_lua_fields(&r->r.insert.t); + break; + case TNT_OP_DELETE: + tc_printf("delete("); + tc_printf("%"PRIu32", ", r->r.del.h.ns); + tc_print_lua_tuple(&r->r.del.t); + break; + case TNT_OP_UPDATE: + tc_printf("update("); + tc_printf("%"PRIu32", ", r->r.update.h.ns); + tc_print_lua_tuple(&r->r.update.t); + tc_printf(", '"); + for (uint32_t i = 0; i < r->r.update.opc; i++) { + switch (r->r.update.opv[i].op) { + case TNT_UPDATE_ASSIGN: + tc_printf("=p"); + break; + case TNT_UPDATE_ADD: + tc_printf("+p"); + break; + case TNT_UPDATE_AND: + tc_printf("&p"); + break; + case TNT_UPDATE_XOR: + tc_printf("^p"); + break; + case TNT_UPDATE_OR: + tc_printf("|p"); + break; + case TNT_UPDATE_SPLICE: + tc_printf(":p"); + break; + case TNT_UPDATE_DELETE: + tc_printf("#p"); + break; + case TNT_UPDATE_INSERT: + tc_printf("!p"); + break; + } + } + tc_printf("'"); + for (uint32_t i = 0; i < r->r.update.opc; i++) { + tc_printf(", %"PRIu32, + r->r.update.opv[i].field); + switch (r->r.update.opv[i].op){ + case TNT_UPDATE_ADD: + case TNT_UPDATE_AND: + case TNT_UPDATE_XOR: + case TNT_UPDATE_OR: + tc_printf(", "); + tc_print_lua_field(r->r.update.opv[i].data, + r->r.update.opv[i].size, 0); + break; + case TNT_UPDATE_SPLICE: + tc_printf(", box.pack('ppp'"); + char *data = r->r.update.opv[i].data; + size_t pos = 1; + tc_printf(", %"PRId32, + *(int32_t *)(data + pos)); + pos += 5; + tc_printf(", %"PRId32", ", + *(int32_t *)(data + pos)); + pos += 4 + r->r.update.opv[i].size_enc_len; + tc_printf("\'"); + tc_print_string(data, + r->r.update.opv[i].size - pos, 1); + tc_printf("\'"); + tc_printf(")"); + break; + case TNT_UPDATE_DELETE: + tc_printf(", \'\'"); + break; + case TNT_UPDATE_INSERT: + case TNT_UPDATE_ASSIGN: + tc_printf(", "); + tc_print_lua_field(r->r.update.opv[i].data, + r->r.update.opv[i].size, + tc.opt.str_instead_int); + break; + } + } + break; + } + tc_printf(") -- %"PRIu64, hdr->lsn); + if (tc.opt.delim_len > 0) + tc_printf("%s\n", tc.opt.delim); + else + tc_printf("\n"); +} + +tc_printerf_xlog_t tc_print_getxlogcb(const char *name) +{ + if (name == NULL) + return tc_printer_xlog_tarantool; + if (!strcasecmp(name, "tarantool")) + return tc_printer_xlog_tarantool; + if (!strcasecmp(name, "raw")) + return tc_printer_xlog_raw; + if (!strcasecmp(name, "lua")) + return tc_printer_xlog_lua; + return NULL; +} diff --git a/client/tarantool/tc_print_xlog.h b/client/tarantool/tc_print_xlog.h new file mode 100644 index 0000000000000000000000000000000000000000..e9918606ee7d3278b3d3206f0aa23b404410c7d2 --- /dev/null +++ b/client/tarantool/tc_print_xlog.h @@ -0,0 +1,37 @@ +#ifndef TC_PRINT_XLOG_H_INCLUDED +#define TC_PRINT_XLOG_H_INCLUDED +/* + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``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 + * <COPYRIGHT HOLDER> OR CONTRIBUTORS 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. + */ + +typedef void (*tc_printerf_xlog_t)(struct tnt_log_header_v11 *hdr, + struct tnt_request *r); + +tc_printerf_xlog_t tc_print_getxlogcb(const char *name); + +#endif /* TC_PRINT_XLOG_H_INCLUDED */ diff --git a/client/tarantool/tc_store.c b/client/tarantool/tc_store.c index c04d8c481b2fdd4c1149d51e65e486d3597bc8b4..ec2bc020e7b259ceebf9f49e53f5ab7b53b65f0f 100644 --- a/client/tarantool/tc_store.c +++ b/client/tarantool/tc_store.c @@ -44,6 +44,8 @@ #include "client/tarantool/tc_admin.h" #include "client/tarantool/tc.h" #include "client/tarantool/tc_print.h" +#include "client/tarantool/tc_print_xlog.h" +#include "client/tarantool/tc_print_snap.h" #include "client/tarantool/tc_query.h" #include "client/tarantool/tc_store.h" @@ -125,7 +127,7 @@ static int tc_store_printer(struct tnt_iter *i) { return 0; struct tnt_stream_xlog *s = TNT_SXLOG_CAST(TNT_IREQUEST_STREAM(i)); - ((tc_printerf_t)tc.opt.printer)(&s->log.current.hdr, r); + ((tc_printerf_xlog_t)tc.opt.xlog_printer)(&s->log.current.hdr, r); return 0; } @@ -133,21 +135,7 @@ static int tc_snapshot_printer(struct tnt_iter *i) { struct tnt_tuple *tu = TNT_ISTORAGE_TUPLE(i); struct tnt_stream_snapshot *ss = TNT_SSNAPSHOT_CAST(TNT_ISTORAGE_STREAM(i)); - if (tc.opt.raw) { - if (tc.opt.raw_with_headers) { - fwrite(&tnt_log_marker_v11, - sizeof(tnt_log_marker_v11), 1, stdout); - } - fwrite(&ss->log.current.row_snap, - sizeof(ss->log.current.row_snap), 1, stdout); - fwrite(tu->data, tu->size, 1, stdout); - } else { - tc_printf("tag: %"PRIu16", cookie: %"PRIu64", space: %"PRIu32"\n", - ss->log.current.row_snap.tag, - ss->log.current.row_snap.cookie, - ss->log.current.row_snap.space); - tc_print_tuple(tu); - } + ((tc_printerf_snap_t)tc.opt.snap_printer)(&ss->log.current.row_snap, tu); return 0; } @@ -200,6 +188,7 @@ int tc_store_cat(void) fputs("\n", stdout); } int rc; + switch (type) { case TNT_LOG_SNAPSHOT: rc = tc_store_foreach_snapshot(tc_snapshot_printer); diff --git a/cmake/CheckBuiltInFunctionExists.cmake b/cmake/CheckBuiltInFunctionExists.cmake deleted file mode 100644 index 6b4128f129f2f3effa521edcd17508964b3a03bd..0000000000000000000000000000000000000000 --- a/cmake/CheckBuiltInFunctionExists.cmake +++ /dev/null @@ -1,7 +0,0 @@ -include(CheckCSourceCompiles) - -macro(check_builtin_function_exists function variable) - set(CMAKE_REQUIRED_FLAGS "-Wno-unused-value -Wno-error") - check_c_source_compiles("int main(void) { ${function}; return 0; }" - ${variable}) -endmacro(check_builtin_function_exists) diff --git a/connector/c/include/tp.h b/connector/c/include/tp.h index 4d886719bb2fe7515e8528bf57daefc05ff0e770..d76e827be6d569bcb59fca7b3abe92a5f0d5e670 100644 --- a/connector/c/include/tp.h +++ b/connector/c/include/tp.h @@ -329,6 +329,12 @@ tp_unused(struct tp *p) { * data must be manually freed when the buffer is no longer * needed. * (eg. free(p->s)); + * if realloc will return NULL, then you must destroy previous memory. + * (eg. + * if (tp_realloc(p, ..) == NULL) { + * free(p->s) + * return NULL; + * } */ tp_function_unused static char* tp_realloc(struct tp *p, size_t required, size_t *size) { diff --git a/connector/c/tnt/tnt_buf.c b/connector/c/tnt/tnt_buf.c index c5209dd1b2effcaf52220e88dc7828d7cf831941..a67016197d2de33feb4637f56886f0bfa8f98b25 100644 --- a/connector/c/tnt/tnt_buf.c +++ b/connector/c/tnt/tnt_buf.c @@ -68,8 +68,10 @@ static char* tnt_buf_resize(struct tnt_stream *s, size_t size) { size_t off = sb->size; size_t nsize = off + size; char *nd = realloc(sb->data, nsize); - if (nd == NULL) + if (nd == NULL) { + free(sb->data); return NULL; + } sb->data = nd; sb->size = nsize; return sb->data + off; diff --git a/connector/c/tnt/tnt_tuple.c b/connector/c/tnt/tnt_tuple.c index 0a9c36baf7451ed8da96ff71d2708e8d44e7e950..9b8cec4a6ae2aeaee781fcef811e80193b55551f 100644 --- a/connector/c/tnt/tnt_tuple.c +++ b/connector/c/tnt/tnt_tuple.c @@ -394,6 +394,7 @@ struct tnt_tuple *tnt_list_at(struct tnt_list *l, struct tnt_tuple *t) { /* reallocating tuple data */ char *ndata = realloc(l->list, sizeof(struct tnt_list_ptr) * (l->count + 1)); if (ndata == NULL) { + free(l->list); if (allocated) tnt_tuple_free(t); return NULL; diff --git a/doc/user/iterator-types.xml b/doc/user/iterator-types.xml index 836442e21826f5a4d7e017f5e559ccb8fa6148e8..efb50c86a364555ede0a602469d59968ac9e09e6 100644 --- a/doc/user/iterator-types.xml +++ b/doc/user/iterator-types.xml @@ -55,7 +55,7 @@ </simpara> <simpara> Semantics of the match depends on the index. - A HASH and TREE index only supports exact match: all parts + A HASH index only supports exact match: all parts of a key participating in the index must be provided. In case of TREE index, only few parts of a key or a key prefix are accepted for search. @@ -64,12 +64,10 @@ criteria. </simpara> <simpara> - A non-unique HASH index returns tuples in unspecified - order. When a TREE index is not unique, or only part of a key is given as a search criteria, matching tuples are returned in ascending order. - BITSET indexes are always unique. + BITSET and HASH indexes are always unique. </simpara> </entry> </row> diff --git a/doc/user/space.xml b/doc/user/space.xml index 348d21151f205f5f453f0cb73c267ceae4845c78..655af9e063fd7b85ca69943f1940a9e63425c3b0 100644 --- a/doc/user/space.xml +++ b/doc/user/space.xml @@ -94,8 +94,7 @@ please be aware of these restrictions: dynamically, currently you need to restart the server even to disable or enable a space, </simpara></listitem> - <listitem><simpara>HASH indexes may cover only one field and can - not be non-unique. + <listitem><simpara>HASH indexes can not be non-unique. </simpara></listitem> </itemizedlist> </para> diff --git a/include/mhash.h b/include/mhash.h index 722a9dffc9e973c038a056336a9524595200e07d..e6bfc1943896cc35fffa1589125b72cda194803f 100644 --- a/include/mhash.h +++ b/include/mhash.h @@ -170,6 +170,32 @@ _mh(next_slot)(mh_int_t slot, mh_int_t inc, mh_int_t size) return slot >= size ? slot - size : slot; } +#if defined(mh_hash_key) && defined(mh_eq_key) +/** + * If it is necessary to search by something different + * than a hash node, define mh_hash_key and mh_eq_key + * and use mh_find(). + */ +static inline mh_int_t +_mh(find)(struct _mh(t) *h, mh_key_t key, mh_arg_t arg) +{ + (void) arg; + + mh_int_t k = mh_hash_key(key, arg); + mh_int_t i = k % h->n_buckets; + mh_int_t inc = 1 + k % (h->n_buckets - 1); + for (;;) { + if ((mh_exist(h, i) && mh_eq_key(key, mh_node(h, i), arg))) + return i; + + if (!mh_dirty(h, i)) + return h->n_buckets; + + i = _mh(next_slot)(i, inc, h->n_buckets); + } +} +#endif + static inline mh_int_t _mh(get)(struct _mh(t) *h, const mh_node_t *node, mh_arg_t arg) @@ -504,9 +530,12 @@ _mh(dump)(struct _mh(t) *h) #undef mh_int_t #undef mh_node_t #undef mh_arg_t +#undef mh_key_t #undef mh_name #undef mh_hash +#undef mh_hash_key #undef mh_eq +#undef mh_eq_key #undef mh_node #undef mh_dirty #undef mh_place diff --git a/src/box/box_lua.cc b/src/box/box_lua.cc index 589d5f12be8da19cec67208aeba4df1ca72127ea..5679d16a17dd73e8efbac52e4d7c487a3ae4df92 100644 --- a/src/box/box_lua.cc +++ b/src/box/box_lua.cc @@ -159,7 +159,7 @@ lbox_tuple_slice(struct lua_State *L) } else if (offset < 0 && -offset <= tuple->field_count) { start = offset + tuple->field_count; } else { - luaL_error(L, "tuple.slice(): start >= field count"); + return luaL_error(L, "tuple.slice(): start >= field count"); } if (argc == 2) { @@ -169,13 +169,13 @@ lbox_tuple_slice(struct lua_State *L) } else if (offset < 0 && -offset < tuple->field_count) { end = offset + tuple->field_count; } else { - luaL_error(L, "tuple.slice(): end > field count"); + return luaL_error(L, "tuple.slice(): end > field count"); } } else { end = tuple->field_count; } if (end <= start) - luaL_error(L, "tuple.slice(): start must be less than end"); + return luaL_error(L, "tuple.slice(): start must be less than end"); struct tuple_iterator it; tuple_rewind(&it, tuple); diff --git a/src/box/hash_index.cc b/src/box/hash_index.cc index bb88876104dabd550e556dc97310c359412fdd51..4b726caf25bef92cb292909768e972de1ca73bba 100644 --- a/src/box/hash_index.cc +++ b/src/box/hash_index.cc @@ -32,75 +32,125 @@ #include "pickle.h" #include "exception.h" #include "space.h" -#include "assoc.h" #include "errinj.h" -/* {{{ HashIndex Iterators ****************************************/ - -struct hash_i32_iterator { - struct iterator base; /* Must be the first member. */ - struct mh_i32ptr_t *hash; - mh_int_t h_pos; -}; - -struct hash_i64_iterator { - struct iterator base; - struct mh_i64ptr_t *hash; - mh_int_t h_pos; -}; +#include "third_party/PMurHash.h" -struct hash_lstr_iterator { - struct iterator base; - struct mh_lstrptr_t *hash; - mh_int_t h_pos; +enum { + HASH_SEED = 13U }; -void -hash_iterator_free(struct iterator *iterator) +static inline bool +mh_index_eq(struct tuple *const *tuple_a, struct tuple *const *tuple_b, + const struct key_def *key_def) { - assert(iterator->free == hash_iterator_free); - free(iterator); + return tuple_compare(*tuple_a, *tuple_b, key_def) == 0; } -struct tuple * -hash_iterator_i32_ge(struct iterator *ptr) +static inline bool +mh_index_eq_key(const char *key, struct tuple *const *tuple, + const struct key_def *key_def) { - assert(ptr->free == hash_iterator_free); - struct hash_i32_iterator *it = (struct hash_i32_iterator *) ptr; + return tuple_compare_with_key(*tuple, key, key_def->part_count, + key_def) == 0; +} - while (it->h_pos < mh_end(it->hash)) { - if (mh_exist(it->hash, it->h_pos)) - return (struct tuple *) mh_i32ptr_node(it->hash, it->h_pos++)->val; - it->h_pos++; + +static inline uint32_t +mh_index_hash(struct tuple *const *tuple, const struct key_def *key_def) +{ + struct key_part *part = key_def->parts; + uint32_t size; + /* + * Speed up the simplest case when we have a + * single-part hash over an integer field. + */ + if (key_def->part_count == 1 && part->type == NUM) + return *(uint32_t *) tuple_field(*tuple, part->fieldno, &size); + + uint32_t h = HASH_SEED; + uint32_t carry = 0; + uint32_t total_size = 0; + + for ( ; part < key_def->parts + key_def->part_count; part++) { + const char *field = tuple_field(*tuple, part->fieldno, &size); + assert(size < INT32_MAX); + PMurHash32_Process(&h, &carry, field, size); + total_size += size; } - return NULL; + + return PMurHash32_Result(h, carry, total_size); } -struct tuple * -hash_iterator_i64_ge(struct iterator *ptr) +static inline uint32_t +mh_index_hash_key(const char *key, const struct key_def *key_def) { - assert(ptr->free == hash_iterator_free); - struct hash_i64_iterator *it = (struct hash_i64_iterator *) ptr; + struct key_part *part = key_def->parts; - while (it->h_pos < mh_end(it->hash)) { - if (mh_exist(it->hash, it->h_pos)) - return (struct tuple *) mh_i64ptr_node( - it->hash,it->h_pos++)->val; - it->h_pos++; + if (key_def->part_count == 1 && part->type == NUM) { + (void) load_varint32(&key); + return *(uint32_t *) key; } - return NULL; + uint32_t h = HASH_SEED; + uint32_t carry = 0; + uint32_t total_size = 0; + + for ( ; part < key_def->parts + key_def->part_count; part++) { + uint32_t size = load_varint32(&key); + if (part->type == NUM64 && size == sizeof(uint32_t)) { + /* Allow search in NUM64 indexes using NUM keys. */ + uint64_t u64 = *(uint32_t *) key; + PMurHash32_Process(&h, &carry, &u64, sizeof(uint64_t)); + total_size += sizeof(uint64_t); + } else { + assert(size < INT32_MAX); + PMurHash32_Process(&h, &carry, key, size); + total_size += size; + } + key += size; + } + + return PMurHash32_Result(h, carry, total_size); +} + +#define mh_int_t uint32_t +#define mh_arg_t const struct key_def * + +#define mh_hash(a, arg) mh_index_hash(a, arg) +#define mh_hash_key(a, arg) mh_index_hash_key(a, arg) +#define mh_eq(a, b, arg) mh_index_eq(a, b, arg) +#define mh_eq_key(a, b, arg) mh_index_eq_key(a, b, arg) + +#define mh_key_t const char * +typedef struct tuple * mh_node_t; +#define mh_name _index +#define MH_SOURCE 1 +#include <mhash.h> + +/* {{{ HashIndex Iterators ****************************************/ + +struct hash_iterator { + struct iterator base; /* Must be the first member. */ + struct mh_index_t *hash; + uint32_t h_pos; +}; + +void +hash_iterator_free(struct iterator *iterator) +{ + assert(iterator->free == hash_iterator_free); + free(iterator); } struct tuple * -hash_iterator_lstr_ge(struct iterator *ptr) +hash_iterator_ge(struct iterator *ptr) { assert(ptr->free == hash_iterator_free); - struct hash_lstr_iterator *it = (struct hash_lstr_iterator *) ptr; + struct hash_iterator *it = (struct hash_iterator *) ptr; while (it->h_pos < mh_end(it->hash)) { if (mh_exist(it->hash, it->h_pos)) - return (struct tuple *) mh_lstrptr_node( - it->hash,it->h_pos++)->val; + return *mh_index_node(it->hash, it->h_pos++); it->h_pos++; } return NULL; @@ -113,147 +163,29 @@ hash_iterator_eq_next(struct iterator *it __attribute__((unused))) } static struct tuple * -hash_iterator_i32_eq(struct iterator *it) -{ - it->next = hash_iterator_eq_next; - return hash_iterator_i32_ge(it); -} - -static struct tuple * -hash_iterator_i64_eq(struct iterator *it) -{ - it->next = hash_iterator_eq_next; - return hash_iterator_i64_ge(it); -} - -static struct tuple * -hash_iterator_lstr_eq(struct iterator *it) +hash_iterator_eq(struct iterator *it) { it->next = hash_iterator_eq_next; - return hash_iterator_lstr_ge(it); + return hash_iterator_ge(it); } /* }}} */ /* {{{ HashIndex -- base class for all hashes. ********************/ -class Hash32Index: public HashIndex { -public: - Hash32Index(struct key_def *key_def, struct space *space); - ~Hash32Index(); - - virtual size_t - size() const; - - virtual struct tuple * - random(u32 rnd) const; - - virtual struct tuple * - findByKey(const char *key, u32 part_count) const; - - virtual struct tuple * - replace(struct tuple *old_tuple, struct tuple *new_tuple, - enum dup_replace_mode mode); - - struct iterator * - allocIterator() const; - - void - initIterator(struct iterator *iterator, enum iterator_type type, - const char *key, u32 part_count) const; - - virtual void - reserve(u32 n_tuples); -private: - struct mh_i32ptr_t *int_hash; -}; - -class Hash64Index: public HashIndex { -public: - Hash64Index(struct key_def *key_def, struct space *space); - ~Hash64Index(); - - virtual size_t - size() const; - - virtual struct tuple * - random(u32 rnd) const; - - virtual struct tuple * - findByKey(const char *key, u32 part_count) const; - - virtual struct tuple * - replace(struct tuple *old_tuple, struct tuple *new_tuple, - enum dup_replace_mode mode); - - struct iterator * - allocIterator() const; - - void - initIterator(struct iterator *iterator, enum iterator_type type, - const char *key, u32 part_count) const; - - virtual void - reserve(u32 n_tuples); -private: - struct mh_i64ptr_t *int64_hash; -}; - -class HashStrIndex: public HashIndex { -public: - HashStrIndex(struct key_def *key_def, struct space *space); - ~HashStrIndex(); - - virtual size_t - size() const; - - virtual struct tuple * - random(u32 rnd) const; - - virtual struct tuple * - findByKey(const char *key, u32 part_count) const; - - virtual struct tuple * - replace(struct tuple *old_tuple, struct tuple *new_tuple, - enum dup_replace_mode mode); - - struct iterator * - allocIterator() const; - - void - initIterator(struct iterator *iterator, enum iterator_type type, - const char *key, u32 part_count) const; - - virtual void - reserve(u32 n_tuples); -private: - struct mh_lstrptr_t *str_hash; -}; - -HashIndex * -HashIndex::factory(struct key_def *key_def, struct space *space) +HashIndex::HashIndex(struct key_def *key_def, struct space *space) + : Index(key_def, space) { - /* - * Hash index always has a single-field key. - */ - switch (key_def->parts[0].type) { - case NUM: - return new Hash32Index(key_def, space); /* 32-bit integer hash */ - case NUM64: - return new Hash64Index(key_def, space); /* 64-bit integer hash */ - case STRING: - return new HashStrIndex(key_def, space); /* string hash */ - default: - assert(false); + hash = mh_index_new(); + if (hash == NULL) { + tnt_raise(ClientError, ER_MEMORY_ISSUE, sizeof(hash), + "HashIndex", "hash"); } - - return NULL; } -HashIndex::HashIndex(struct key_def *key_def, struct space *space) - : Index(key_def, space) +HashIndex::~HashIndex() { - /* Nothing */ + mh_index_delete(hash); } void @@ -293,299 +225,84 @@ HashIndex::build(Index *pk) replace(NULL, tuple, DUP_INSERT); } -struct tuple * -HashIndex::min() const -{ - tnt_raise(ClientError, ER_UNSUPPORTED, "Hash index", "min()"); - return NULL; -} - -struct tuple * -HashIndex::max() const -{ - tnt_raise(ClientError, ER_UNSUPPORTED, "Hash index", "max()"); - return NULL; -} - - - -/* }}} */ - -/* {{{ Hash32Index ************************************************/ - -static inline struct mh_i32ptr_node_t -int32_key_to_node(const char *key) -{ - u32 key_size = load_varint32(&key); - if (key_size != 4) - tnt_raise(ClientError, ER_KEY_FIELD_TYPE, "u32"); - struct mh_i32ptr_node_t node = { *(u32 *) key, NULL }; - return node; -} - -static inline struct mh_i32ptr_node_t -int32_tuple_to_node(struct tuple *tuple, struct key_def *key_def) -{ - const char *field = tuple_field_old(tuple, key_def->parts[0].fieldno); - struct mh_i32ptr_node_t node = int32_key_to_node(field); - node.val = tuple; - return node; -} - - -Hash32Index::Hash32Index(struct key_def *key_def, struct space *space) - : HashIndex(key_def, space) -{ - int_hash = mh_i32ptr_new(); -} - -Hash32Index::~Hash32Index() -{ - mh_i32ptr_delete(int_hash); -} - void -Hash32Index::reserve(u32 n_tuples) +HashIndex::reserve(u32 n_tuples) { - mh_i32ptr_reserve(int_hash, n_tuples, NULL); + mh_index_reserve(hash, n_tuples, key_def); } size_t -Hash32Index::size() const +HashIndex::size() const { - return mh_size(int_hash); + return mh_size(hash); } struct tuple * -Hash32Index::random(u32 rnd) const +HashIndex::min() const { - mh_int_t k = mh_i32ptr_random(int_hash, rnd); - if (k != mh_end(int_hash)) - return (struct tuple *) mh_i32ptr_node(int_hash, k)->val; + tnt_raise(ClientError, ER_UNSUPPORTED, "Hash index", "min()"); return NULL; } struct tuple * -Hash32Index::findByKey(const char *key, u32 part_count) const -{ - assert(key_def->is_unique && part_count == key_def->part_count); - struct tuple *ret = NULL; - struct mh_i32ptr_node_t node = int32_key_to_node(key); - mh_int_t k = mh_i32ptr_get(int_hash, &node, NULL); - if (k != mh_end(int_hash)) - ret = (struct tuple *) mh_i32ptr_node(int_hash, k)->val; -#ifdef DEBUG - say_debug("Hash32Index find(self:%p, key:%i) = %p", self, node.key, ret); -#endif - return ret; -} - -struct tuple * -Hash32Index::replace(struct tuple *old_tuple, struct tuple *new_tuple, - enum dup_replace_mode mode) -{ - struct mh_i32ptr_node_t new_node, old_node; - uint32_t errcode; - - if (new_tuple) { - struct mh_i32ptr_node_t *dup_node = &old_node; - new_node = int32_tuple_to_node(new_tuple, key_def); - mh_int_t pos = mh_i32ptr_put(int_hash, &new_node, - &dup_node, NULL); - - ERROR_INJECT(ERRINJ_INDEX_ALLOC, - { - mh_i32ptr_del(int_hash, pos, NULL); - pos = mh_end(int_hash); - }); - - if (pos == mh_end(int_hash)) { - tnt_raise(LoggedError, ER_MEMORY_ISSUE, (ssize_t) pos, - "int hash", "key"); - } - struct tuple *dup_tuple = (dup_node ? - (struct tuple *) dup_node->val - : NULL); - errcode = replace_check_dup(old_tuple, dup_tuple, mode); - - if (errcode) { - mh_i32ptr_remove(int_hash, &new_node, NULL); - if (dup_node) { - pos = mh_i32ptr_put(int_hash, dup_node, - NULL, NULL); - if (pos == mh_end(int_hash)) { - panic("Failed to allocate memory in " - "recover of int hash"); - } - } - tnt_raise(ClientError, errcode, index_n(this)); - } - if (dup_tuple) - return dup_tuple; - } - if (old_tuple) { - old_node = int32_tuple_to_node(old_tuple, key_def); - mh_i32ptr_remove(int_hash, &old_node, NULL); - } - return old_tuple; -} - - -struct iterator * -Hash32Index::allocIterator() const -{ - struct hash_i32_iterator *it = (struct hash_i32_iterator *) - malloc(sizeof(struct hash_i32_iterator)); - if (it) { - memset(it, 0, sizeof(*it)); - it->base.next = hash_iterator_i32_ge; - it->base.free = hash_iterator_free; - } - return (struct iterator *) it; -} - -void -Hash32Index::initIterator(struct iterator *ptr, enum iterator_type type, - const char *key, u32 part_count) const -{ - assert ((part_count == 1 && key != NULL) || part_count == 0); - (void) part_count; - assert(ptr->free == hash_iterator_free); - - struct hash_i32_iterator *it = (struct hash_i32_iterator *) ptr; - struct mh_i32ptr_node_t node; - - switch (type) { - case ITER_GE: - if (key != NULL) { - node = int32_key_to_node(key); - it->h_pos = mh_i32ptr_get(int_hash, &node, NULL); - it->base.next = hash_iterator_i32_ge; - break; - } - /* Fall through. */ - case ITER_ALL: - it->h_pos = mh_begin(int_hash); - it->base.next = hash_iterator_i32_ge; - break; - case ITER_EQ: - node = int32_key_to_node(key); - it->h_pos = mh_i32ptr_get(int_hash, &node, NULL); - it->base.next = hash_iterator_i32_eq; - break; - default: - tnt_raise(ClientError, ER_UNSUPPORTED, - "Hash index", "requested iterator type"); - } - it->hash = int_hash; -} - -/* }}} */ - -/* {{{ Hash64Index ************************************************/ - -static inline struct mh_i64ptr_node_t -int64_key_to_node(const char *key) -{ - u32 key_size = load_varint32(&key); - if (key_size != 8) - tnt_raise(ClientError, ER_KEY_FIELD_TYPE, "u64"); - struct mh_i64ptr_node_t node = { *(u64 *) key, NULL }; - return node; -} - -static inline struct mh_i64ptr_node_t -int64_tuple_to_node(struct tuple *tuple, struct key_def *key_def) -{ - const char *field = tuple_field_old(tuple, key_def->parts[0].fieldno); - struct mh_i64ptr_node_t node = int64_key_to_node(field); - node.val = tuple; - return node; -} - -Hash64Index::Hash64Index(struct key_def *key_def, struct space *space) - : HashIndex(key_def, space) -{ - int64_hash = mh_i64ptr_new(); -} - -Hash64Index::~Hash64Index() -{ - mh_i64ptr_delete(int64_hash); -} - -void -Hash64Index::reserve(u32 n_tuples) -{ - mh_i64ptr_reserve(int64_hash, n_tuples, NULL); -} - -size_t -Hash64Index::size() const +HashIndex::max() const { - return mh_size(int64_hash); + tnt_raise(ClientError, ER_UNSUPPORTED, "Hash index", "max()"); + return NULL; } struct tuple * -Hash64Index::random(u32 rnd) const +HashIndex::random(u32 rnd) const { - mh_int_t k = mh_i64ptr_random(int64_hash, rnd); - if (k != mh_end(int64_hash)) - return (struct tuple *) mh_i64ptr_node(int64_hash, k)->val; + uint32_t k = mh_index_random(hash, rnd); + if (k != mh_end(hash)) + return *mh_index_node(hash, k); return NULL; } struct tuple * -Hash64Index::findByKey(const char *key, u32 part_count) const +HashIndex::findByKey(const char *key, u32 part_count) const { assert(key_def->is_unique && part_count == key_def->part_count); - (void) part_count; struct tuple *ret = NULL; - struct mh_i64ptr_node_t node = int64_key_to_node(key); - mh_int_t k = mh_i64ptr_get(int64_hash, &node, NULL); - if (k != mh_end(int64_hash)) - ret = (struct tuple *) mh_i64ptr_node(int64_hash, k)->val; -#ifdef DEBUG - say_debug("Hash64Index find(self:%p, key:%i) = %p", self, node.key, ret); -#endif + uint32_t k = mh_index_find(hash, key, key_def); + if (k != mh_end(hash)) + ret = *mh_index_node(hash, k); return ret; } struct tuple * -Hash64Index::replace(struct tuple *old_tuple, struct tuple *new_tuple, - enum dup_replace_mode mode) +HashIndex::replace(struct tuple *old_tuple, struct tuple *new_tuple, + enum dup_replace_mode mode) { - struct mh_i64ptr_node_t new_node, old_node; uint32_t errcode; if (new_tuple) { - struct mh_i64ptr_node_t *dup_node = &old_node; - new_node = int64_tuple_to_node(new_tuple, key_def); - mh_int_t pos = mh_i64ptr_put(int64_hash, &new_node, - &dup_node, NULL); + struct tuple *dup_tuple = NULL; + struct tuple **dup_node = &dup_tuple; + uint32_t pos = mh_index_put(hash, &new_tuple, + &dup_node, key_def); ERROR_INJECT(ERRINJ_INDEX_ALLOC, { - mh_i64ptr_del(int64_hash, pos, NULL); - pos = mh_end(int64_hash); + mh_index_del(hash, pos, key_def); + pos = mh_end(hash); }); - if (pos == mh_end(int64_hash)) { + if (pos == mh_end(hash)) { tnt_raise(LoggedError, ER_MEMORY_ISSUE, (ssize_t) pos, - "int hash", "key"); + "hash", "key"); } - struct tuple *dup_tuple = (dup_node ? - (struct tuple *) dup_node->val : - NULL); errcode = replace_check_dup(old_tuple, dup_tuple, mode); if (errcode) { - mh_i64ptr_remove(int64_hash, &new_node, NULL); - if (dup_node) { - pos = mh_i64ptr_put(int64_hash, dup_node, NULL, NULL); - if (pos == mh_end(int64_hash)) { + mh_index_remove(hash, &new_tuple, key_def); + if (dup_tuple) { + pos = mh_index_put(hash, &dup_tuple, NULL, + key_def); + if (pos == mh_end(hash)) { panic("Failed to allocate memory in " "recover of int hash"); } @@ -598,229 +315,57 @@ Hash64Index::replace(struct tuple *old_tuple, struct tuple *new_tuple, } if (old_tuple) { - old_node = int64_tuple_to_node(old_tuple, key_def); - mh_i64ptr_remove(int64_hash, &old_node, NULL); + mh_index_remove(hash, &old_tuple, key_def); } - return old_tuple; } - struct iterator * -Hash64Index::allocIterator() const -{ - struct hash_i64_iterator *it = (struct hash_i64_iterator *) - malloc(sizeof(struct hash_i64_iterator)); - if (it) { - memset(it, 0, sizeof(*it)); - it->base.next = hash_iterator_i64_ge; - it->base.free = hash_iterator_free; +HashIndex::allocIterator() const +{ + struct hash_iterator *it = (struct hash_iterator *) + calloc(1, sizeof(*it)); + if (it == NULL) { + tnt_raise(ClientError, ER_MEMORY_ISSUE, + sizeof(struct hash_iterator), + "HashIndex", "iterator"); } + it->base.next = hash_iterator_ge; + it->base.free = hash_iterator_free; return (struct iterator *) it; } void -Hash64Index::initIterator(struct iterator *ptr, enum iterator_type type, - const char *key, u32 part_count) const +HashIndex::initIterator(struct iterator *ptr, enum iterator_type type, + const char *key, u32 part_count) const { - assert ((part_count == 1 && key != NULL) || part_count == 0); + assert (key != NULL || part_count == 0); (void) part_count; assert(ptr->free == hash_iterator_free); - struct hash_i64_iterator *it = (struct hash_i64_iterator *) ptr; - struct mh_i64ptr_node_t node; - switch (type) { - case ITER_GE: - if (key != NULL) { - node = int64_key_to_node(key); - it->h_pos = mh_i64ptr_get(int64_hash, &node, NULL); - it->base.next = hash_iterator_i64_ge; - break; - } - /* Fall through. */ - case ITER_ALL: - it->h_pos = mh_begin(int64_hash); - it->base.next = hash_iterator_i64_ge; - break; - case ITER_EQ: - node = int64_key_to_node(key); - it->h_pos = mh_i64ptr_get(int64_hash, &node, NULL); - it->base.next = hash_iterator_i64_eq; - break; - default: - tnt_raise(ClientError, ER_UNSUPPORTED, - "Hash index", "requested iterator type"); - } - it->hash = int64_hash; -} - -/* }}} */ - -/* {{{ HashStrIndex ***********************************************/ - -static inline struct mh_lstrptr_node_t -lstrptr_tuple_to_node(struct tuple *tuple, struct key_def *key_def) -{ - const char *field = tuple_field_old(tuple, key_def->parts[0].fieldno); - if (field == NULL) - tnt_raise(ClientError, ER_NO_SUCH_FIELD, - key_def->parts[0].fieldno); - - struct mh_lstrptr_node_t node = { field, tuple }; - return node; -} - -HashStrIndex::HashStrIndex(struct key_def *key_def, struct space *space) - : HashIndex(key_def, space) -{ - str_hash = mh_lstrptr_new(); -} - -HashStrIndex::~HashStrIndex() -{ - mh_lstrptr_delete(str_hash); -} - -void -HashStrIndex::reserve(u32 n_tuples) -{ - mh_lstrptr_reserve(str_hash, n_tuples, NULL); -} - - -size_t -HashStrIndex::size() const -{ - return mh_size(str_hash); -} - -struct tuple * -HashStrIndex::random(u32 rnd) const -{ - mh_int_t k = mh_lstrptr_random(str_hash, rnd); - if (k != mh_end(str_hash)) - return (struct tuple *) mh_lstrptr_node(str_hash, k)->val; - return NULL; -} - -struct tuple * -HashStrIndex::findByKey(const char *key, u32 part_count) const -{ - assert(key_def->is_unique && part_count == key_def->part_count); - (void) part_count; - - struct tuple *ret = NULL; - const struct mh_lstrptr_node_t node = { key, NULL }; - mh_int_t k = mh_lstrptr_get(str_hash, &node, NULL); - if (k != mh_end(str_hash)) - ret = (struct tuple *) mh_lstrptr_node(str_hash, k)->val; -#ifdef DEBUG - u32 key_size = load_varint32(&key); - say_debug("HashStrIndex find(self:%p, key:(%i)'%.*s') = %p", - self, key_size, key_size, key, ret); -#endif - return ret; -} - -struct tuple * -HashStrIndex::replace(struct tuple *old_tuple, struct tuple *new_tuple, - enum dup_replace_mode mode) -{ - struct mh_lstrptr_node_t new_node, old_node; - uint32_t errcode; - - if (new_tuple) { - struct mh_lstrptr_node_t *dup_node = &old_node; - new_node = lstrptr_tuple_to_node(new_tuple, key_def); - mh_int_t pos = mh_lstrptr_put(str_hash, &new_node, - &dup_node, NULL); - - ERROR_INJECT(ERRINJ_INDEX_ALLOC, - { - mh_lstrptr_del(str_hash, pos, NULL); - pos = mh_end(str_hash); - }); - - if (pos == mh_end(str_hash)) { - tnt_raise(LoggedError, ER_MEMORY_ISSUE, (ssize_t) pos, - "str hash", "key"); - } - struct tuple *dup_tuple = dup_node - ? (struct tuple *) dup_node->val - : NULL; - errcode = replace_check_dup(old_tuple, dup_tuple, mode); - - if (errcode) { - mh_lstrptr_remove(str_hash, &new_node, NULL); - if (dup_node) { - pos = mh_lstrptr_put(str_hash, dup_node, - NULL, NULL); - if (pos == mh_end(str_hash)) { - panic("Failed to allocate memory in " - "recover of str hash"); - } - } - tnt_raise(ClientError, errcode, index_n(this)); - } - if (dup_tuple) - return dup_tuple; - } - if (old_tuple) { - old_node = lstrptr_tuple_to_node(old_tuple, key_def); - mh_lstrptr_remove(str_hash, &old_node, NULL); - } - return old_tuple; -} - -struct iterator * -HashStrIndex::allocIterator() const -{ - struct hash_lstr_iterator *it = (struct hash_lstr_iterator *) - malloc(sizeof(struct hash_lstr_iterator)); - if (it) { - memset(it, 0, sizeof(*it)); - it->base.next = hash_iterator_lstr_ge; - it->base.free = hash_iterator_free; - } - return (struct iterator *) it; -} - -void -HashStrIndex::initIterator(struct iterator *ptr, enum iterator_type type, - const char *key, u32 part_count) const -{ - assert ((part_count == 1 && key != NULL) || part_count == 0); - (void) part_count; - - assert(ptr->free == hash_iterator_free); - struct hash_lstr_iterator *it = (struct hash_lstr_iterator *) ptr; - struct mh_lstrptr_node_t node; + struct hash_iterator *it = (struct hash_iterator *) ptr; switch (type) { case ITER_GE: if (key != NULL) { - node.key = key; - it->h_pos = mh_lstrptr_get(str_hash, &node, NULL); - it->base.next = hash_iterator_lstr_ge; + it->h_pos = mh_index_find(hash, key, key_def); + it->base.next = hash_iterator_ge; break; } /* Fall through. */ case ITER_ALL: - it->base.next = hash_iterator_lstr_ge; - it->h_pos = mh_begin(str_hash); + it->h_pos = mh_begin(hash); + it->base.next = hash_iterator_ge; break; case ITER_EQ: - node.key = key; - it->h_pos = mh_lstrptr_get(str_hash, &node, NULL); - it->base.next = hash_iterator_lstr_eq; + it->h_pos = mh_index_find(hash, key, key_def); + it->base.next = hash_iterator_eq; break; default: tnt_raise(ClientError, ER_UNSUPPORTED, "Hash index", "requested iterator type"); } - it->hash = str_hash; + it->hash = hash; } - /* }}} */ - diff --git a/src/box/hash_index.h b/src/box/hash_index.h index 409d171cb2cf08321d209dec7870447c47d5f1e6..b315e72ae9057e235b3e3e9b572ca6109324a837 100644 --- a/src/box/hash_index.h +++ b/src/box/hash_index.h @@ -31,33 +31,35 @@ #include "index.h" +struct mh_index_t; class HashIndex: public Index { public: - static HashIndex * - factory(struct key_def *key_def, struct space *space); - HashIndex(struct key_def *key_def, struct space *space); + ~HashIndex(); virtual void beginBuild(); virtual void buildNext(struct tuple *tuple); virtual void endBuild(); virtual void build(Index *pk); - virtual size_t size() const = 0; + virtual size_t size() const; virtual struct tuple *min() const; virtual struct tuple *max() const; - virtual struct tuple *random(u32 rnd) const = 0; - virtual struct tuple *findByKey(const char *key, u32 part_count) const = 0; + virtual struct tuple *random(u32 rnd) const; + virtual struct tuple *findByKey(const char *key, u32 part_count) const; virtual struct tuple *replace(struct tuple *old_tuple, struct tuple *new_tuple, - enum dup_replace_mode mode) = 0; + enum dup_replace_mode mode); - virtual struct iterator *allocIterator() const = 0; + virtual struct iterator *allocIterator() const; virtual void initIterator(struct iterator *iterator, enum iterator_type type, - const char *key, u32 part_count) const = 0; + const char *key, u32 part_count) const; + + virtual void reserve(u32 n_tuples); - virtual void reserve(u32 n_tuples) = 0; +protected: + struct mh_index_t *hash; }; #endif /* TARANTOOL_BOX_HASH_INDEX_H_INCLUDED */ diff --git a/src/box/index.cc b/src/box/index.cc index b5e07081fdeaee9acb36d7601fd5c804b7bb70a1..9a7c212c5c6a254df8237dc12ce37e803ce5f976 100644 --- a/src/box/index.cc +++ b/src/box/index.cc @@ -143,7 +143,7 @@ Index::factory(enum index_type type, struct key_def *key_def, struct space *spac { switch (type) { case HASH: - return HashIndex::factory(key_def, space); + return new HashIndex(key_def, space); case TREE: return new TreeIndex(key_def, space); case BITSET: diff --git a/src/box/space.cc b/src/box/space.cc index dc9eb92017f1d9a74ecb3514e291fd0f9e264f9d..90b6e3f3eb00b082b646b443dbf278fd8be52c3c 100644 --- a/src/box/space.cc +++ b/src/box/space.cc @@ -586,12 +586,6 @@ check_spaces(struct tarantool_cfg *conf) switch (index_type) { case HASH: /* check hash index */ - /* hash index must has single-field key */ - if (key_part_count != 1) { - out_warning(CNF_OK, "(space = %zu index = %zu) " - "hash index must has a single-field key", i, j); - return -1; - } /* hash index must be unique */ if (!index->unique) { out_warning(CNF_OK, "(space = %zu index = %zu) " diff --git a/src/log_io.cc b/src/log_io.cc index fdecb1840de8bde4357ef22c15040ce837ebb72f..ff1c45ff659085dd0f34fcd1fc747275e51cfe54 100644 --- a/src/log_io.cc +++ b/src/log_io.cc @@ -588,12 +588,16 @@ log_io_open(struct log_dir *dir, enum log_mode mode, l->dir = dir; l->is_inprogress = suffix == INPROGRESS; if (mode == LOG_READ) { - if (log_io_verify_meta(l, &errmsg) != 0) + if (log_io_verify_meta(l, &errmsg) != 0) { + errmsg = strerror(errno); goto error; + } } else { /* LOG_WRITE */ setvbuf(l->f, NULL, _IONBF, 0); - if (log_io_write_header(l) != 0) + if (log_io_write_header(l) != 0) { + errmsg = strerror(errno); goto error; + } } return l; error: diff --git a/test/big/hash.result b/test/big/hash.result index 6099e94a9c3c51624d3f20c588b009316310a63f..1734cc3de78fc9df1f894d1266f36c53b15695d5 100644 --- a/test/big/hash.result +++ b/test/big/hash.result @@ -156,25 +156,41 @@ error: 'Invalid key part count in an exact match (expected 1, got 2)' # Insert valid fieds -lua box.space[11]:insert('00000000', 'value1 v1.0', 'value2 v1.0') +lua box.space[11]:insert(0ULL, 'value1 v1.0', 'value2 v1.0') --- - - 3472328296227680304: {'value1 v1.0', 'value2 v1.0'} + - 0: {'value1 v1.0', 'value2 v1.0'} ... -lua box.space[11]:insert('00000001', 'value1 v1.0', 'value2 v1.0') +lua box.space[11]:insert(1ULL, 'value1 v1.0', 'value2 v1.0') --- - - 3544385890265608240: {'value1 v1.0', 'value2 v1.0'} + - 1: {'value1 v1.0', 'value2 v1.0'} ... -lua box.space[11]:insert('00000002', 'value1 v1.0', 'value2 v1.0') +lua box.space[11]:insert(2ULL, 'value1 v1.0', 'value2 v1.0') --- - - 3616443484303536176: {'value1 v1.0', 'value2 v1.0'} + - 2: {'value1 v1.0', 'value2 v1.0'} ... -lua box.space[11]:insert('00000003', 'value1 v1.0', 'value2 v1.0') +lua box.space[11]:insert(3ULL, 'value1 v1.0', 'value2 v1.0') --- - - 3688501078341464112: {'value1 v1.0', 'value2 v1.0'} + - 3: {'value1 v1.0', 'value2 v1.0'} ... # Insert invalid fields +lua box.space[11]:insert(100, 'value1 v1.0', 'value2 v1.0') +--- +error: 'Supplied key field type does not match index type: expected u64' +... +lua box.space[11]:insert(101, 'value1 v1.0', 'value2 v1.0') +--- +error: 'Supplied key field type does not match index type: expected u64' +... +lua box.space[11]:insert(102, 'value1 v1.0', 'value2 v1.0') +--- +error: 'Supplied key field type does not match index type: expected u64' +... +lua box.space[11]:insert(103, 'value1 v1.0', 'value2 v1.0') +--- +error: 'Supplied key field type does not match index type: expected u64' +... lua box.space[11]:insert('invalid key', 'value1 v1.0', 'value2 v1.0') --- error: 'Supplied key field type does not match index type: expected u64' @@ -187,24 +203,36 @@ error: 'Supplied key field type does not match index type: expected u64' # Replace valid fieds -lua box.space[11]:replace('00000003', 'value1 v1.31', 'value2 1.12') +lua box.space[11]:replace(3ULL, 'value1 v1.31', 'value2 1.12') --- - - 3688501078341464112: {'value1 v1.31', 'value2 1.12'} + - 3: {'value1 v1.31', 'value2 1.12'} ... -lua box.space[11]:replace('00000001', 'value1 v1.32', 'value2 1.72') +lua box.space[11]:replace(1ULL, 'value1 v1.32', 'value2 1.72') --- - - 3544385890265608240: {'value1 v1.32', 'value2 1.72'} + - 1: {'value1 v1.32', 'value2 1.72'} ... -lua box.space[11]:replace('00000002', 'value1 v1.43', 'value2 1.92') +lua box.space[11]:replace(2ULL, 'value1 v1.43', 'value2 1.92') --- - - 3616443484303536176: {'value1 v1.43', 'value2 1.92'} + - 2: {'value1 v1.43', 'value2 1.92'} ... # Replace invalid fields -lua box.space[10]:replace('invalid key', 'value1 v1.0', 'value2 v1.0') +lua box.space[11]:replace(3, 'value1 v1.31', 'value2 1.12') --- -error: 'Supplied key field type does not match index type: expected u32' +error: 'Supplied key field type does not match index type: expected u64' +... +lua box.space[11]:replace(1, 'value1 v1.32', 'value2 1.72') +--- +error: 'Supplied key field type does not match index type: expected u64' +... +lua box.space[11]:replace(2, 'value1 v1.43', 'value2 1.92') +--- +error: 'Supplied key field type does not match index type: expected u64' +... +lua box.space[11]:replace('invalid key', 'value1 v1.0', 'value2 v1.0') +--- +error: 'Supplied key field type does not match index type: expected u64' ... #-----------------------------------------------------------------------------# @@ -214,26 +242,51 @@ error: 'Supplied key field type does not match index type: expected u32' # select by valid keys -lua box.space[11]:select(0, '00000000') +lua box.space[11]:select(0, 0ULL) --- - - 3472328296227680304: {'value1 v1.0', 'value2 v1.0'} + - 0: {'value1 v1.0', 'value2 v1.0'} ... -lua box.space[11]:select(0, '00000001') +lua box.space[11]:select(0, 1ULL) --- - - 3544385890265608240: {'value1 v1.32', 'value2 1.72'} + - 1: {'value1 v1.32', 'value2 1.72'} ... -lua box.space[11]:select(0, '00000002') +lua box.space[11]:select(0, 2ULL) --- - - 3616443484303536176: {'value1 v1.43', 'value2 1.92'} + - 2: {'value1 v1.43', 'value2 1.92'} ... -lua box.space[11]:select(0, '00000003') +lua box.space[11]:select(0, 3ULL) --- - - 3688501078341464112: {'value1 v1.31', 'value2 1.12'} + - 3: {'value1 v1.31', 'value2 1.12'} ... -lua box.space[11]:select(0, '00000004') +lua box.space[11]:select(0, 4ULL) --- ... -lua box.space[11]:select(0, '00000005') +lua box.space[11]:select(0, 5ULL) +--- +... + +# select by valid NUM keys + +lua box.space[11]:select(0, 0) +--- + - 0: {'value1 v1.0', 'value2 v1.0'} +... +lua box.space[11]:select(0, 1) +--- + - 1: {'value1 v1.32', 'value2 1.72'} +... +lua box.space[11]:select(0, 2) +--- + - 2: {'value1 v1.43', 'value2 1.92'} +... +lua box.space[11]:select(0, 3) +--- + - 3: {'value1 v1.31', 'value2 1.12'} +... +lua box.space[11]:select(0, 4) +--- +... +lua box.space[11]:select(0, 5) --- ... @@ -255,26 +308,67 @@ error: 'Invalid key part count (expected [0..1], got 2)' # delete by valid keys -lua box.space[11]:delete('00000000') +lua box.space[11]:delete(0ULL) +--- + - 0: {'value1 v1.0', 'value2 v1.0'} +... +lua box.space[11]:delete(1ULL) +--- + - 1: {'value1 v1.32', 'value2 1.72'} +... +lua box.space[11]:delete(2ULL) --- - - 3472328296227680304: {'value1 v1.0', 'value2 v1.0'} + - 2: {'value1 v1.43', 'value2 1.92'} ... -lua box.space[11]:delete('00000001') +lua box.space[11]:delete(3ULL) --- - - 3544385890265608240: {'value1 v1.32', 'value2 1.72'} + - 3: {'value1 v1.31', 'value2 1.12'} ... -lua box.space[11]:delete('00000002') +lua box.space[11]:delete(4ULL) --- - - 3616443484303536176: {'value1 v1.43', 'value2 1.92'} ... -lua box.space[11]:delete('00000003') +lua box.space[11]:delete(5ULL) +--- +... +lua box.space[11]:insert(0ULL, 'value1 v1.0', 'value2 v1.0') +--- + - 0: {'value1 v1.0', 'value2 v1.0'} +... +lua box.space[11]:insert(1ULL, 'value1 v1.0', 'value2 v1.0') --- - - 3688501078341464112: {'value1 v1.31', 'value2 1.12'} + - 1: {'value1 v1.0', 'value2 v1.0'} +... +lua box.space[11]:insert(2ULL, 'value1 v1.0', 'value2 v1.0') +--- + - 2: {'value1 v1.0', 'value2 v1.0'} +... +lua box.space[11]:insert(3ULL, 'value1 v1.0', 'value2 v1.0') +--- + - 3: {'value1 v1.0', 'value2 v1.0'} +... + +# delete by valid NUM keys + +lua box.space[11]:delete(0) +--- + - 0: {'value1 v1.0', 'value2 v1.0'} +... +lua box.space[11]:delete(1) +--- + - 1: {'value1 v1.0', 'value2 v1.0'} +... +lua box.space[11]:delete(2) +--- + - 2: {'value1 v1.0', 'value2 v1.0'} +... +lua box.space[11]:delete(3) +--- + - 3: {'value1 v1.0', 'value2 v1.0'} ... -lua box.space[11]:delete('00000004') +lua box.space[11]:delete(4) --- ... -lua box.space[11]:delete('00000005') +lua box.space[11]:delete(5) --- ... diff --git a/test/big/hash.test b/test/big/hash.test index 51c46574092d540d1b34acf510427594c2ee57da..79b510266fb3c45aa865205e710497c77712f985 100644 --- a/test/big/hash.test +++ b/test/big/hash.test @@ -107,17 +107,20 @@ print """ print """ # Insert valid fieds """ -exec admin "lua box.space[11]:insert('00000000', 'value1 v1.0', 'value2 v1.0')" -exec admin "lua box.space[11]:insert('00000001', 'value1 v1.0', 'value2 v1.0')" -exec admin "lua box.space[11]:insert('00000002', 'value1 v1.0', 'value2 v1.0')" -exec admin "lua box.space[11]:insert('00000003', 'value1 v1.0', 'value2 v1.0')" +exec admin "lua box.space[11]:insert(0ULL, 'value1 v1.0', 'value2 v1.0')" +exec admin "lua box.space[11]:insert(1ULL, 'value1 v1.0', 'value2 v1.0')" +exec admin "lua box.space[11]:insert(2ULL, 'value1 v1.0', 'value2 v1.0')" +exec admin "lua box.space[11]:insert(3ULL, 'value1 v1.0', 'value2 v1.0')" print """ # Insert invalid fields """ +exec admin "lua box.space[11]:insert(100, 'value1 v1.0', 'value2 v1.0')" +exec admin "lua box.space[11]:insert(101, 'value1 v1.0', 'value2 v1.0')" +exec admin "lua box.space[11]:insert(102, 'value1 v1.0', 'value2 v1.0')" +exec admin "lua box.space[11]:insert(103, 'value1 v1.0', 'value2 v1.0')" exec admin "lua box.space[11]:insert('invalid key', 'value1 v1.0', 'value2 v1.0')" - print """ #-----------------------------------------------------------------------------# # 64-bit hash replace fields tests @@ -127,15 +130,17 @@ print """ print """ # Replace valid fieds """ -exec admin "lua box.space[11]:replace('00000003', 'value1 v1.31', 'value2 1.12')" -exec admin "lua box.space[11]:replace('00000001', 'value1 v1.32', 'value2 1.72')" -exec admin "lua box.space[11]:replace('00000002', 'value1 v1.43', 'value2 1.92')" +exec admin "lua box.space[11]:replace(3ULL, 'value1 v1.31', 'value2 1.12')" +exec admin "lua box.space[11]:replace(1ULL, 'value1 v1.32', 'value2 1.72')" +exec admin "lua box.space[11]:replace(2ULL, 'value1 v1.43', 'value2 1.92')" print """ # Replace invalid fields """ -exec admin "lua box.space[10]:replace('invalid key', 'value1 v1.0', 'value2 v1.0')" - +exec admin "lua box.space[11]:replace(3, 'value1 v1.31', 'value2 1.12')" +exec admin "lua box.space[11]:replace(1, 'value1 v1.32', 'value2 1.72')" +exec admin "lua box.space[11]:replace(2, 'value1 v1.43', 'value2 1.92')" +exec admin "lua box.space[11]:replace('invalid key', 'value1 v1.0', 'value2 v1.0')" print """ #-----------------------------------------------------------------------------# @@ -146,12 +151,22 @@ print """ print """ # select by valid keys """ -exec admin "lua box.space[11]:select(0, '00000000')" -exec admin "lua box.space[11]:select(0, '00000001')" -exec admin "lua box.space[11]:select(0, '00000002')" -exec admin "lua box.space[11]:select(0, '00000003')" -exec admin "lua box.space[11]:select(0, '00000004')" -exec admin "lua box.space[11]:select(0, '00000005')" +exec admin "lua box.space[11]:select(0, 0ULL)" +exec admin "lua box.space[11]:select(0, 1ULL)" +exec admin "lua box.space[11]:select(0, 2ULL)" +exec admin "lua box.space[11]:select(0, 3ULL)" +exec admin "lua box.space[11]:select(0, 4ULL)" +exec admin "lua box.space[11]:select(0, 5ULL)" + +print """ +# select by valid NUM keys +""" +exec admin "lua box.space[11]:select(0, 0)" +exec admin "lua box.space[11]:select(0, 1)" +exec admin "lua box.space[11]:select(0, 2)" +exec admin "lua box.space[11]:select(0, 3)" +exec admin "lua box.space[11]:select(0, 4)" +exec admin "lua box.space[11]:select(0, 5)" print """ # select by invalid keys @@ -159,7 +174,6 @@ print """ exec admin "lua box.space[11]:select(0, 'invalid key')" exec admin "lua box.space[11]:select(0, '00000001', '00000002')" - print """ #-----------------------------------------------------------------------------# # 64-bit hash delete fields test @@ -169,12 +183,27 @@ print """ print """ # delete by valid keys """ -exec admin "lua box.space[11]:delete('00000000')" -exec admin "lua box.space[11]:delete('00000001')" -exec admin "lua box.space[11]:delete('00000002')" -exec admin "lua box.space[11]:delete('00000003')" -exec admin "lua box.space[11]:delete('00000004')" -exec admin "lua box.space[11]:delete('00000005')" +exec admin "lua box.space[11]:delete(0ULL)" +exec admin "lua box.space[11]:delete(1ULL)" +exec admin "lua box.space[11]:delete(2ULL)" +exec admin "lua box.space[11]:delete(3ULL)" +exec admin "lua box.space[11]:delete(4ULL)" +exec admin "lua box.space[11]:delete(5ULL)" + +exec admin "lua box.space[11]:insert(0ULL, 'value1 v1.0', 'value2 v1.0')" +exec admin "lua box.space[11]:insert(1ULL, 'value1 v1.0', 'value2 v1.0')" +exec admin "lua box.space[11]:insert(2ULL, 'value1 v1.0', 'value2 v1.0')" +exec admin "lua box.space[11]:insert(3ULL, 'value1 v1.0', 'value2 v1.0')" + +print """ +# delete by valid NUM keys +""" +exec admin "lua box.space[11]:delete(0)" +exec admin "lua box.space[11]:delete(1)" +exec admin "lua box.space[11]:delete(2)" +exec admin "lua box.space[11]:delete(3)" +exec admin "lua box.space[11]:delete(4)" +exec admin "lua box.space[11]:delete(5)" print """ # delete by invalid keys diff --git a/test/big/hash_multipart.result b/test/big/hash_multipart.result new file mode 100644 index 0000000000000000000000000000000000000000..9c2a07d05c84664fa7db991ebaa02cf16bbca93a --- /dev/null +++ b/test/big/hash_multipart.result @@ -0,0 +1,73 @@ +insert into t27 values (0, 'foo', 0, '', 1) +Insert OK, 1 row affected +insert into t27 values (0, 'foo', 1, '', 1) +Insert OK, 1 row affected +insert into t27 values (1, 'foo', 0, '', 2) +Insert OK, 1 row affected +insert into t27 values (1, 'foo', 1, '', 2) +Insert OK, 1 row affected +insert into t27 values (0, 'bar', 0, '', 3) +Insert OK, 1 row affected +insert into t27 values (0, 'bar', 1, '', 3) +Insert OK, 1 row affected +insert into t27 values (1, 'bar', 0, '', 4) +Insert OK, 1 row affected +insert into t27 values (1, 'bar', 1, '', 4) +Insert OK, 1 row affected +insert into t27 values (1, 'bar', 1, '', 5) +An error occurred: ER_TUPLE_FOUND, 'Duplicate key exists in unique index 0' +lua function box.select_all(space) space = tonumber(space) local result = {} for k, v in box.space[space]:pairs() do table.insert(result, v) end return unpack(result) end +--- +... +call box.select_all('27') +Found 8 tuples: +[0, 'bar', 0, '', 3] +[0, 'bar', 1, '', 3] +[0, 'foo', 0, '', 1] +[0, 'foo', 1, '', 1] +[1, 'bar', 0, '', 4] +[1, 'bar', 1, '', 4] +[1, 'foo', 0, '', 2] +[1, 'foo', 1, '', 2] +lua box.select(27, 0, 1, 'foo', 0) +--- + - 1: {'foo', 0, '', 2} +... +lua box.select(27, 0, 1, 'bar', 0) +--- + - 1: {'bar', 0, '', 4} +... +lua box.select(27, 0, 1, 'foo') +--- +error: 'Invalid key part count in an exact match (expected 3, got 2)' +... +lua box.select(27, 0, 1, 'foo', 0, 0) +--- +error: 'Invalid key part count (expected [0..3], got 4)' +... +lua box.select(27, 0, 1, 'foo', 'baz') +--- +error: 'Supplied key field type does not match index type: expected u32' +... +lua box.select(27, 1, 1, 4) +--- + - 1: {'bar', 1, '', 4} +... +lua box.select(27, 1, 1, 5) +--- +... +lua box.select(27, 1, 1) +--- +error: 'Invalid key part count in an exact match (expected 2, got 1)' +... +lua box.select(27, 1, 1, 'baz') +--- +error: 'Supplied key field type does not match index type: expected u32' +... +lua box.space[27]:truncate() +--- +... +lua box.space[27]:len() +--- + - 0 +... diff --git a/test/big/hash_multipart.test b/test/big/hash_multipart.test new file mode 100644 index 0000000000000000000000000000000000000000..8d4f7347a588802520bd1e4dc9ccda5cf93f3ed6 --- /dev/null +++ b/test/big/hash_multipart.test @@ -0,0 +1,53 @@ +# encoding: tarantool +# + +# insert rows +exec sql "insert into t27 values (0, 'foo', 0, '', 1)" +exec sql "insert into t27 values (0, 'foo', 1, '', 1)" +exec sql "insert into t27 values (1, 'foo', 0, '', 2)" +exec sql "insert into t27 values (1, 'foo', 1, '', 2)" +exec sql "insert into t27 values (0, 'bar', 0, '', 3)" +exec sql "insert into t27 values (0, 'bar', 1, '', 3)" +exec sql "insert into t27 values (1, 'bar', 0, '', 4)" +exec sql "insert into t27 values (1, 'bar', 1, '', 4)" +# try to insert a row with a duplicate key +exec sql "insert into t27 values (1, 'bar', 1, '', 5)" + +# output all rows +lua_code = """ +function box.select_all(space) + space = tonumber(space) + local result = {} + for k, v in box.space[space]:pairs() do + table.insert(result, v) + end + return unpack(result) +end +""" +exec admin "lua " + lua_code.replace('\n', ' ') +sql.sort = True +exec sql "call box.select_all('27')" +sql.sort = False + +# primary index select +exec admin "lua box.select(27, 0, 1, 'foo', 0)" +exec admin "lua box.select(27, 0, 1, 'bar', 0)" +# primary index slect with missing part +exec admin "lua box.select(27, 0, 1, 'foo')" +# primary index slect with extra part +exec admin "lua box.select(27, 0, 1, 'foo', 0, 0)" +# primary index select with wrong type +exec admin "lua box.select(27, 0, 1, 'foo', 'baz')" + +# secondary index select +exec admin "lua box.select(27, 1, 1, 4)" +# secondary index select with no such key +exec admin "lua box.select(27, 1, 1, 5)" +# secondary index select with missing part +exec admin "lua box.select(27, 1, 1)" +# secondary index select with wrong type +exec admin "lua box.select(27, 1, 1, 'baz')" + +# cleanup +exec admin "lua box.space[27]:truncate()" +exec admin "lua box.space[27]:len()" diff --git a/test/big/iterator.result b/test/big/iterator.result index 21609efd1f75cbdd77a41fc7e0513989b4c3f294..1c832db3fdf2f25c8526613911879a6a91c76c10 100644 --- a/test/big/iterator.result +++ b/test/big/iterator.result @@ -768,7 +768,7 @@ error: 'utils.lua:27: Key part count 4 is greater than index part count 2' ... #-----------------------------------------------------------------------------# -# Iterator: hash multi-part non-unique +# Iterator: hash single-part unique #-----------------------------------------------------------------------------# lua iterate(20, 4, 0, 1) @@ -840,6 +840,68 @@ lua iterate(20, 4, 0, 1, box.index.GE, 'pid_999') sorted output ... +#-----------------------------------------------------------------------------# +# Iterator: hash multi-part unique +#-----------------------------------------------------------------------------# + +lua iterate(20, 5, 1, 3, box.index.ALL) +--- +sorted output +$sid_001$tid_997$ +$sid_001$tid_998$ +$sid_002$tid_996$ +$sid_002$tid_997$ +$sid_003$tid_996$ +$sid_004$tid_996$ +$sid_005$tid_994$ +$sid_005$tid_995$ +$sid_005$tid_996$ +$sid_006$tid_996$ +... +lua iterate(20, 5, 1, 3, box.index.EQ) +--- +error: 'Invalid key part count in an exact match (expected 2, got 0)' +... +lua iterate(20, 5, 1, 3, box.index.EQ, 'sid_005') +--- +error: 'Invalid key part count in an exact match (expected 2, got 1)' +... +lua iterate(20, 5, 1, 3, box.index.GE) +--- +sorted output +$sid_001$tid_997$ +$sid_001$tid_998$ +$sid_002$tid_996$ +$sid_002$tid_997$ +$sid_003$tid_996$ +$sid_004$tid_996$ +$sid_005$tid_994$ +$sid_005$tid_995$ +$sid_005$tid_996$ +$sid_006$tid_996$ +... +lua iterate(20, 2, 1, 3, box.index.EQ, 'sid_005', 'tid_995') +--- +$sid_005$tid_995$ +... +lua iterate(20, 2, 1, 3, box.index.EQ, 'sid_005', 'tid_999') +--- +... +lua iterate(20, 2, 1, 3, box.index.EQ, 'sid_005', 'tid_995', 'a') +--- +error: 'utils.lua:27: Key part count 3 is greater than index part count 2' +... +lua iterate(20, 2, 1, 3, box.index.GE, 'sid_005', 'tid_995') +--- +$sid_005$tid_995$ +$sid_005$tid_996$ +$sid_006$tid_996$ +... +lua iterate(20, 2, 1, 3, box.index.GE, 'sid_005', 'tid_999') +--- +$sid_006$tid_996$ +... + #-----------------------------------------------------------------------------# # Iterator: various #-----------------------------------------------------------------------------# diff --git a/test/big/iterator.test b/test/big/iterator.test index 53fd5531bce1051cd2e18a455fad100f1f20f723..2a737ee5b490526e48d84efa3574598ff8e0a045 100644 --- a/test/big/iterator.test +++ b/test/big/iterator.test @@ -135,7 +135,7 @@ exec admin "lua iterate(20, 3, 2, 4, box.index.LT, 'tid_996', 'to', 'many', 'key print """ #-----------------------------------------------------------------------------# -# Iterator: hash multi-part non-unique +# Iterator: hash single-part unique #-----------------------------------------------------------------------------# """ @@ -148,6 +148,21 @@ exec admin "lua iterate(20, 4, 0, 1, box.index.EQ, 'pid_666')" exec admin "lua iterate(20, 4, 0, 1, box.index.GE, 'pid_001')" exec admin "lua iterate(20, 4, 0, 1, box.index.GE, 'pid_999')" +print """ +#-----------------------------------------------------------------------------# +# Iterator: hash multi-part unique +#-----------------------------------------------------------------------------# +""" +exec admin "lua iterate(20, 5, 1, 3, box.index.ALL)" +exec admin "lua iterate(20, 5, 1, 3, box.index.EQ)" +exec admin "lua iterate(20, 5, 1, 3, box.index.EQ, 'sid_005')" +exec admin "lua iterate(20, 5, 1, 3, box.index.GE)" +exec admin "lua iterate(20, 2, 1, 3, box.index.EQ, 'sid_005', 'tid_995')" +exec admin "lua iterate(20, 2, 1, 3, box.index.EQ, 'sid_005', 'tid_999')" +exec admin "lua iterate(20, 2, 1, 3, box.index.EQ, 'sid_005', 'tid_995', 'a')" +exec admin "lua iterate(20, 2, 1, 3, box.index.GE, 'sid_005', 'tid_995')" +exec admin "lua iterate(20, 2, 1, 3, box.index.GE, 'sid_005', 'tid_999')" + print """ #-----------------------------------------------------------------------------# # Iterator: various diff --git a/test/big/tarantool.cfg b/test/big/tarantool.cfg index 0f9f416055bc0a9060f935ceca19c73b0fdb1141..58def14511efd800f7beeb9f97ac5e19329b1e7d 100644 --- a/test/big/tarantool.cfg +++ b/test/big/tarantool.cfg @@ -283,6 +283,14 @@ space[20].index[4].unique = 1 space[20].index[4].key_field[0].fieldno = 0 space[20].index[4].key_field[0].type = "STR" +# Hash multi-part unique +space[20].index[5].type = "HASH" +space[20].index[5].unique = 1 +space[20].index[5].key_field[0].fieldno = 1 +space[20].index[5].key_field[0].type = "STR" +space[20].index[5].key_field[1].fieldno = 2 +space[20].index[5].key_field[1].type = "STR" + # hash::replace space[21].enabled = true @@ -367,3 +375,22 @@ space[26].index[1].type = "HASH" space[26].index[1].unique = true space[26].index[1].key_field[0].fieldno = 0 space[26].index[1].key_field[0].type = "NUM" + +# Multi-part hash +space[27].enabled = 1 + +space[27].index[0].type = HASH +space[27].index[0].unique = 1 +space[27].index[0].key_field[0].fieldno = 0 +space[27].index[0].key_field[0].type = NUM +space[27].index[0].key_field[1].fieldno = 1 +space[27].index[0].key_field[1].type = STR +space[27].index[0].key_field[2].fieldno = 2 +space[27].index[0].key_field[2].type = NUM + +space[27].index[1].type = HASH +space[27].index[1].unique = 1 +space[27].index[1].key_field[0].fieldno = 2 +space[27].index[1].key_field[0].type = NUM +space[27].index[1].key_field[1].fieldno = 4 +space[27].index[1].key_field[1].type = NUM