Changeset: 35dd9dee1374 for MonetDB URL: https://dev.monetdb.org/hg/MonetDB/rev/35dd9dee1374 Branch: default Log Message:
Merged client_interrupts branch into default. diffs (truncated from 8089 to 300 lines): diff --git a/clients/ChangeLog b/clients/ChangeLog --- a/clients/ChangeLog +++ b/clients/ChangeLog @@ -1,3 +1,12 @@ # ChangeLog file for clients # This file is updated with Maddlog +* Wed Jan 10 2024 Sjoerd Mullender <sjo...@acm.org> +- Implemented interrupt handling in mclient. When using mclient + interactively, an interrupt (usually control-C) stops whatever the + client is doing. When editing a line, the line is discarded; when + editing a second or later line of a query, the whole query is discarded; + when a query is being executed, the server is asked to stop the query + at its earliest convenience. Stopping a running query can only be + done with an up-to-date server. All of this does not work on Windows. + diff --git a/clients/Tests/exports.stable.out b/clients/Tests/exports.stable.out --- a/clients/Tests/exports.stable.out +++ b/clients/Tests/exports.stable.out @@ -681,6 +681,7 @@ MapiMsg mapi_ping(Mapi mid) __attribute_ MapiHdl mapi_prepare(Mapi mid, const char *cmd) __attribute__((__nonnull__(1))); MapiMsg mapi_prepare_handle(MapiHdl hdl, const char *cmd) __attribute__((__nonnull__(1))); MapiHdl mapi_query(Mapi mid, const char *cmd) __attribute__((__nonnull__(1))); +MapiMsg mapi_query_abort(MapiHdl hdl, int reason) __attribute__((__nonnull__(1))); MapiMsg mapi_query_done(MapiHdl hdl) __attribute__((__nonnull__(1))); MapiMsg mapi_query_handle(MapiHdl hdl, const char *cmd) __attribute__((__nonnull__(1))); MapiMsg mapi_query_part(MapiHdl hdl, const char *cmd, size_t size) __attribute__((__nonnull__(1))); @@ -797,7 +798,7 @@ BUN SQLload_file(Client cntxt, Tablet *a str TABLETcollect(BAT **bats, Tablet *as); str TABLETcreate_bats(Tablet *as, BUN est); void TABLETdestroy_format(Tablet *as); -int TABLEToutput_file(Tablet *as, BAT *order, stream *s); +int TABLEToutput_file(Tablet *as, BAT *order, stream *s, bstream *in); int TRACEtable(Client cntxt, BAT **r); int TYPE_xml; int UTF8_strlen(const char *restrict s); @@ -1643,6 +1644,7 @@ stream *block_stream(stream *s); stream *bs_stream(stream *s); bstream *bstream_create(stream *rs, size_t chunk_size); void bstream_destroy(bstream *s); +int bstream_getoob(bstream *s); ssize_t bstream_next(bstream *s); ssize_t bstream_read(bstream *s, size_t size); buffer *buffer_create(size_t size); @@ -1682,12 +1684,14 @@ int mnstr_fsetpos(stream *restrict s, fp int mnstr_fsync(stream *s); buffer *mnstr_get_buffer(stream *s); bool mnstr_get_swapbytes(const stream *s); +int mnstr_getoob(const stream *s); int mnstr_init(void); int mnstr_isalive(const stream *s); bool mnstr_isbinary(const stream *s); const char *mnstr_name(const stream *s); const char *mnstr_peek_error(const stream *s); int mnstr_printf(stream *restrict s, _In_z_ _Printf_format_string_ const char *restrict format, ...) __attribute__((__format__(__printf__, 2, 3))); +int mnstr_putoob(const stream *s, char val); ssize_t mnstr_read(stream *restrict s, void *restrict buf, size_t elmsize, size_t cnt); int mnstr_readBte(stream *restrict s, int8_t *restrict val); int mnstr_readBteArray(stream *restrict s, int8_t *restrict val, size_t cnt); diff --git a/clients/mapiclient/ReadlineTools.c b/clients/mapiclient/ReadlineTools.c --- a/clients/mapiclient/ReadlineTools.c +++ b/clients/mapiclient/ReadlineTools.c @@ -35,6 +35,9 @@ #include <sys/stat.h> #endif +#include <signal.h> +#include <setjmp.h> + static const char *sql_commands[] = { "SELECT", "INSERT", @@ -421,6 +424,30 @@ bailout: return 1; } +static sigjmp_buf readline_jumpbuf; +static volatile sig_atomic_t mayjump; + +void +readline_int_handler(void) +{ + if (mayjump) { + mayjump = false; + siglongjmp(readline_jumpbuf, 1); + } +} + +char * +call_readline(const char *prompt) +{ + char *res; + if (sigsetjmp(readline_jumpbuf, 1) != 0) + return (char *) -1; /* interrupted */ + mayjump = true; + res = readline(prompt); /* normal code path */ + mayjump = false; + return res; +} + void init_readline(Mapi mid, const char *lang, bool save_history) { @@ -501,5 +528,4 @@ save_line(const char *s) append_history(1, _history_file); } - #endif /* HAVE_LIBREADLINE */ diff --git a/clients/mapiclient/ReadlineTools.h b/clients/mapiclient/ReadlineTools.h --- a/clients/mapiclient/ReadlineTools.h +++ b/clients/mapiclient/ReadlineTools.h @@ -22,6 +22,8 @@ void deinit_readline(void); void save_line(const char *s); rl_completion_func_t *suspend_completion(void); void continue_completion(rl_completion_func_t * func); +void readline_int_handler(void); +char *call_readline(const char *prompt); #endif /* HAVE_LIBREADLINE */ #endif /* READLINETOOLS_H_INCLUDED */ diff --git a/clients/mapiclient/mclient.1 b/clients/mapiclient/mclient.1 --- a/clients/mapiclient/mclient.1 +++ b/clients/mapiclient/mclient.1 @@ -107,6 +107,9 @@ To disable reading the file, set the variable .B DOTMONETDBFILE to the empty string in the environment. +.PP +When working interactively, an interrupt (usually control-C) will clear +any query being edited and will stop any running query. .SH OPTIONS .SS General Options diff --git a/clients/mapiclient/mclient.c b/clients/mapiclient/mclient.c --- a/clients/mapiclient/mclient.c +++ b/clients/mapiclient/mclient.c @@ -126,9 +126,9 @@ static timertype t0, t1; /* used for tim #ifdef HAVE_POPEN static char *pager = 0; /* use external pager */ #endif -#ifdef HAVE_SIGACTION -#include <signal.h> /* to block SIGPIPE */ -#endif + +#include <signal.h> + static int rowsperpage = -1; /* for SQL pagination */ static int pagewidth = 0; /* -1: take whatever is necessary, >0: limit */ static int pageheight = 0; /* -1: take whatever is necessary, >0: limit */ @@ -1402,11 +1402,17 @@ SQLpagemove(int *len, int fields, int *p mnstr_printf(toConsole, "next page? (continue,quit,next)"); mnstr_flush(toConsole, MNSTR_FLUSH_DATA); sz = mnstr_readline(fromConsole, buf, sizeof(buf)); - if (sz > 0) { + if (sz < 0 && mnstr_errnr(fromConsole) == MNSTR_INTERRUPT) { + /* interrupted, equivalent to typing 'q' */ + mnstr_clearerr(fromConsole); + mnstr_printf(toConsole, "\n"); + *skiprest = true; + } else if (sz > 0) { if (buf[0] == 'c') *ps = 0; if (buf[0] == 'q') *skiprest = true; + /* make sure we read the whole line */ while (sz > 0 && buf[sz - 1] != '\n') sz = mnstr_readline(fromConsole, buf, sizeof(buf)); } @@ -1414,6 +1420,24 @@ SQLpagemove(int *len, int fields, int *p SQLseparator(len, fields, '-'); } +static volatile sig_atomic_t state; +#define READING 1 +#define WRITING 2 +#define QUERYING 3 +#define IDLING 0 +#define INTERRUPT (-1) + +static void +sigint_handler(int signum) +{ + (void) signum; + + state = INTERRUPT; +#ifdef HAVE_LIBREADLINE + readline_int_handler(); +#endif +} + static void SQLrenderer(MapiHdl hdl) { @@ -1448,6 +1472,15 @@ SQLrenderer(MapiHdl hdl) exit(2); } + if (state == INTERRUPT) { + free(len); + free(hdr); + free(rest); + free(numeric); + return; + } + state = WRITING; + total = 0; lentotal = 0; vartotal = 0; @@ -1628,16 +1661,23 @@ SQLrenderer(MapiHdl hdl) if (ps > 0 && lines >= ps && fromConsole != NULL) { SQLpagemove(len, printfields, &ps, &skiprest); - lines = 0; if (skiprest) { mapi_finish(hdl); break; } + lines = 0; + } + + if (state == INTERRUPT) { + skiprest = true; + mapi_finish(hdl); + break; } nrows++; lines += SQLrow(len, numeric, rest, printfields, 2, 0); } + state = IDLING; if (fields && !skiprest) SQLseparator(len, printfields, '-'); if (skiprest) @@ -1942,6 +1982,8 @@ format_result(Mapi mid, MapiHdl hdl, boo SQLdebugRendering(hdl); continue; } + if (state == INTERRUPT) + break; if (debugMode()) RAWrenderer(hdl); else { @@ -1986,7 +2028,7 @@ format_result(Mapi mid, MapiHdl hdl, boo timerHuman(sqloptimizer, maloptimizer, querytime, singleinstr, false); } - } while (mnstr_errnr(toConsole) == MNSTR_NO__ERROR && (rc = mapi_next_result(hdl)) == 1); + } while (state != INTERRUPT && mnstr_errnr(toConsole) == MNSTR_NO__ERROR && (rc = mapi_next_result(hdl)) == 1); /* * in case we called timerHuman() in the loop above with "total == false", * call it again with "total == true" to get the total wall-clock time @@ -2003,6 +2045,10 @@ format_result(Mapi mid, MapiHdl hdl, boo end_pager(saveFD); #endif + if (state == INTERRUPT) + mnstr_printf(toConsole, "\n"); + state = IDLING; + return rc; } @@ -2092,26 +2138,29 @@ doFileBulk(Mapi mid, stream *fp) length = 0; buf[0] = 0; } else { - if ((length = mnstr_read(fp, buf, 1, bufsize)) < 0) { + while ((length = mnstr_read(fp, buf, 1, bufsize)) < 0) { + if (mnstr_errnr(fp) == MNSTR_INTERRUPT) + continue; /* error */ errseen = true; - break; /* nothing more to do */ + break; + } + if (length < 0) + break; /* nothing more to do */ + buf[length] = 0; + if (length == 0) { + /* end of file */ + if (semicolon2 == 0 && hdl == NULL) + break; /* nothing more to do */ } else { - buf[length] = 0; - if (length == 0) { - /* end of file */ - if (semicolon2 == 0 && hdl == NULL) - break; /* nothing more to do */ - } else { - if (strlen(buf) < (size_t) length) { - mnstr_printf(stderr_stream, "NULL byte in input\n"); - errseen = true; - break; - } - while (length > 1 && buf[length - 1] == ';') { - semicolon1++; - buf[--length] = 0; - } _______________________________________________ checkin-list mailing list -- checkin-list@monetdb.org To unsubscribe send an email to checkin-list-le...@monetdb.org