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/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;