diff --git a/CMakeLists.txt b/CMakeLists.txt index de06226..ec0c3ba 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,7 +14,7 @@ set(PROJECT_NAME usql) include_directories(${CMAKE_SOURCE_DIR}/clib ${CMAKE_SOURCE_DIR}) set(SOURCE - exception.cpp lexer.cpp parser.cpp usql.cpp main.cpp table.cpp table.h row.cpp row.h csvreader.cpp csvreader.h ml_date.cpp clib/ml_string.cpp clib/linenoise.c) + exception.cpp lexer.cpp parser.cpp usql.cpp main.cpp table.cpp table.h row.cpp row.h csvreader.cpp csvreader.h ml_date.cpp settings.cpp clib/ml_string.cpp clib/linenoise.c) add_executable(${PROJECT_NAME} ${SOURCE}) diff --git a/Readme.md b/Readme.md index 561db20..2406075 100644 --- a/Readme.md +++ b/Readme.md @@ -1,15 +1,16 @@ ### TODO +- support for * +- add support for set setting value - command line interface +- date functions - now, add_date... - support for order by, offset, limit (allow column name in order by, validate) - add count min and max functions, eg aggregate functions -- support for uniqueue indexes +- support for uniqueue indexes (primary key) - support for btree indexes - support for parenthesis - functions rtrim, ltrim, rpad, lpad -- support for * - add pipe | token - maybe to create iterator on table -- add exceptions and rename it to UsqlException - class members should have prefix m_ - add const wherever should be diff --git a/clib/linenoise.c b/clib/linenoise.c index 80b3821..87b5fca 100644 --- a/clib/linenoise.c +++ b/clib/linenoise.c @@ -152,25 +152,25 @@ struct linenoiseState { }; enum KEY_ACTION{ - KEY_NULL = 0, /* NULL */ - CTRL_A = 1, /* Ctrl+a */ - CTRL_B = 2, /* Ctrl-b */ - CTRL_C = 3, /* Ctrl-c */ - CTRL_D = 4, /* Ctrl-d */ - CTRL_E = 5, /* Ctrl-e */ - CTRL_F = 6, /* Ctrl-f */ - CTRL_H = 8, /* Ctrl-h */ - TAB = 9, /* Tab */ - CTRL_K = 11, /* Ctrl+k */ - CTRL_L = 12, /* Ctrl+l */ - ENTER = 13, /* Enter */ - CTRL_N = 14, /* Ctrl-n */ - CTRL_P = 16, /* Ctrl-p */ - CTRL_T = 20, /* Ctrl-t */ - CTRL_U = 21, /* Ctrl+u */ - CTRL_W = 23, /* Ctrl+w */ - ESC = 27, /* Escape */ - BACKSPACE = 127 /* Backspace */ + KEY_NULL = 0, /* NULL */ + CTRL_A = 1, /* Ctrl+a */ + CTRL_B = 2, /* Ctrl-b */ + CTRL_C = 3, /* Ctrl-c */ + CTRL_D = 4, /* Ctrl-d */ + CTRL_E = 5, /* Ctrl-e */ + CTRL_F = 6, /* Ctrl-f */ + CTRL_H = 8, /* Ctrl-h */ + TAB = 9, /* Tab */ + CTRL_K = 11, /* Ctrl+k */ + CTRL_L = 12, /* Ctrl+l */ + ENTER = 13, /* Enter */ + CTRL_N = 14, /* Ctrl-n */ + CTRL_P = 16, /* Ctrl-p */ + CTRL_T = 20, /* Ctrl-t */ + CTRL_U = 21, /* Ctrl+u */ + CTRL_W = 23, /* Ctrl+w */ + ESC = 27, /* Escape */ + BACKSPACE = 127 /* Backspace */ }; static void linenoiseAtExit(void); @@ -181,17 +181,17 @@ static void refreshLine(struct linenoiseState *l); #if 0 FILE *lndebug_fp = NULL; #define lndebug(...) \ - do { \ - if (lndebug_fp == NULL) { \ - lndebug_fp = fopen("/tmp/lndebug.txt","a"); \ - fprintf(lndebug_fp, \ - "[%d %d %d] p: %d, rows: %d, rpos: %d, max: %d, oldmax: %d\n", \ - (int)l->len,(int)l->pos,(int)l->oldpos,plen,rows,rpos, \ - (int)l->maxrows,old_rows); \ - } \ - fprintf(lndebug_fp, ", " __VA_ARGS__); \ - fflush(lndebug_fp); \ - } while (0) +do { \ +if (lndebug_fp == NULL) { \ +lndebug_fp = fopen("/tmp/lndebug.txt","a"); \ +fprintf(lndebug_fp, \ +"[%d %d %d] p: %d, rows: %d, rpos: %d, max: %d, oldmax: %d\n", \ +(int)l->len,(int)l->pos,(int)l->oldpos,plen,rows,rpos, \ +(int)l->maxrows,old_rows); \ +} \ +fprintf(lndebug_fp, ", " __VA_ARGS__); \ +fflush(lndebug_fp); \ +} while (0) #else #define lndebug(fmt, ...) #endif @@ -203,156 +203,156 @@ FILE *lndebug_fp = NULL; * number of asterisks, like "****". This is useful for passwords and other * secrets that should not be displayed. */ void linenoiseMaskModeEnable(void) { - maskmode = 1; + maskmode = 1; } /* Disable mask mode. */ void linenoiseMaskModeDisable(void) { - maskmode = 0; + maskmode = 0; } /* Set if to use or not the multi line mode. */ void linenoiseSetMultiLine(int ml) { - mlmode = ml; + mlmode = ml; } /* Return true if the terminal name is in the list of terminals we know are * not able to understand basic escape sequences. */ static int isUnsupportedTerm(void) { - char *term = getenv("TERM"); - int j; + char *term = getenv("TERM"); + int j; - if (term == NULL) return 0; - for (j = 0; unsupported_term[j]; j++) - if (!strcasecmp(term,unsupported_term[j])) return 1; - return 0; + if (term == NULL) return 0; + for (j = 0; unsupported_term[j]; j++) + if (!strcasecmp(term,unsupported_term[j])) return 1; + return 0; } /* Raw mode: 1960 magic shit. */ static int enableRawMode(int fd) { - struct termios raw; + struct termios raw; - if (!isatty(STDIN_FILENO)) goto fatal; - if (!atexit_registered) { - atexit(linenoiseAtExit); - atexit_registered = 1; - } - if (tcgetattr(fd,&orig_termios) == -1) goto fatal; + if (!isatty(STDIN_FILENO)) goto fatal; + if (!atexit_registered) { + atexit(linenoiseAtExit); + atexit_registered = 1; + } + if (tcgetattr(fd,&orig_termios) == -1) goto fatal; - raw = orig_termios; /* modify the original mode */ - /* input modes: no break, no CR to NL, no parity check, no strip char, - * no start/stop output control. */ - raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON); - /* output modes - disable post processing */ - raw.c_oflag &= ~(OPOST); - /* control modes - set 8 bit chars */ - raw.c_cflag |= (CS8); - /* local modes - choing off, canonical off, no extended functions, - * no signal chars (^Z,^C) */ - raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG); - /* control chars - set return condition: min number of bytes and timer. - * We want read to return every single byte, without timeout. */ - raw.c_cc[VMIN] = 1; raw.c_cc[VTIME] = 0; /* 1 byte, no timer */ + raw = orig_termios; /* modify the original mode */ + /* input modes: no break, no CR to NL, no parity check, no strip char, + * no start/stop output control. */ + raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON); + /* output modes - disable post processing */ + raw.c_oflag &= ~(OPOST); + /* control modes - set 8 bit chars */ + raw.c_cflag |= (CS8); + /* local modes - choing off, canonical off, no extended functions, + * no signal chars (^Z,^C) */ + raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG); + /* control chars - set return condition: min number of bytes and timer. + * We want read to return every single byte, without timeout. */ + raw.c_cc[VMIN] = 1; raw.c_cc[VTIME] = 0; /* 1 byte, no timer */ - /* put terminal in raw mode after flushing */ - if (tcsetattr(fd,TCSAFLUSH,&raw) < 0) goto fatal; - rawmode = 1; - return 0; + /* put terminal in raw mode after flushing */ + if (tcsetattr(fd,TCSAFLUSH,&raw) < 0) goto fatal; + rawmode = 1; + return 0; -fatal: - errno = ENOTTY; - return -1; + fatal: + errno = ENOTTY; + return -1; } static void disableRawMode(int fd) { - /* Don't even check the return value as it's too late. */ - if (rawmode && tcsetattr(fd,TCSAFLUSH,&orig_termios) != -1) - rawmode = 0; + /* Don't even check the return value as it's too late. */ + if (rawmode && tcsetattr(fd,TCSAFLUSH,&orig_termios) != -1) + rawmode = 0; } /* Use the ESC [6n escape sequence to query the horizontal cursor position * and return it. On error -1 is returned, on success the position of the * cursor. */ static int getCursorPosition(int ifd, int ofd) { - char buf[32]; - int cols, rows; - unsigned int i = 0; + char buf[32]; + int cols, rows; + unsigned int i = 0; - /* Report cursor location */ - if (write(ofd, "\x1b[6n", 4) != 4) return -1; + /* Report cursor location */ + if (write(ofd, "\x1b[6n", 4) != 4) return -1; - /* Read the response: ESC [ rows ; cols R */ - while (i < sizeof(buf)-1) { - if (read(ifd,buf+i,1) != 1) break; - if (buf[i] == 'R') break; - i++; - } - buf[i] = '\0'; + /* Read the response: ESC [ rows ; cols R */ + while (i < sizeof(buf)-1) { + if (read(ifd,buf+i,1) != 1) break; + if (buf[i] == 'R') break; + i++; + } + buf[i] = '\0'; - /* Parse it. */ - if (buf[0] != ESC || buf[1] != '[') return -1; - if (sscanf(buf+2,"%d;%d",&rows,&cols) != 2) return -1; - return cols; + /* Parse it. */ + if (buf[0] != ESC || buf[1] != '[') return -1; + if (sscanf(buf+2,"%d;%d",&rows,&cols) != 2) return -1; + return cols; } /* Try to get the number of columns in the current terminal, or assume 80 * if it fails. */ static int getColumns(int ifd, int ofd) { - struct winsize ws; + struct winsize ws; - if (ioctl(1, TIOCGWINSZ, &ws) == -1 || ws.ws_col == 0) { - /* ioctl() failed. Try to query the terminal itself. */ - int start, cols; + if (ioctl(1, TIOCGWINSZ, &ws) == -1 || ws.ws_col == 0) { + /* ioctl() failed. Try to query the terminal itself. */ + int start, cols; - /* Get the initial position so we can restore it later. */ - start = getCursorPosition(ifd,ofd); - if (start == -1) goto failed; + /* Get the initial position so we can restore it later. */ + start = getCursorPosition(ifd,ofd); + if (start == -1) goto failed; - /* Go to right margin and get position. */ - if (write(ofd,"\x1b[999C",6) != 6) goto failed; - cols = getCursorPosition(ifd,ofd); - if (cols == -1) goto failed; + /* Go to right margin and get position. */ + if (write(ofd,"\x1b[999C",6) != 6) goto failed; + cols = getCursorPosition(ifd,ofd); + if (cols == -1) goto failed; - /* Restore position. */ - if (cols > start) { - char seq[32]; - snprintf(seq,32,"\x1b[%dD",cols-start); - if (write(ofd,seq,strlen(seq)) == -1) { - /* Can't recover... */ - } - } - return cols; - } else { - return ws.ws_col; - } + /* Restore position. */ + if (cols > start) { + char seq[32]; + snprintf(seq,32,"\x1b[%dD",cols-start); + if (write(ofd,seq,strlen(seq)) == -1) { + /* Can't recover... */ + } + } + return cols; + } else { + return ws.ws_col; + } -failed: - return 80; + failed: + return 80; } /* Clear the screen. Used to handle ctrl+l */ void linenoiseClearScreen(void) { - if (write(STDOUT_FILENO,"\x1b[H\x1b[2J",7) <= 0) { - /* nothing to do, just to avoid warning. */ - } + if (write(STDOUT_FILENO,"\x1b[H\x1b[2J",7) <= 0) { + /* nothing to do, just to avoid warning. */ + } } /* Beep, used for completion when there is nothing to complete or when all * the choices were already shown. */ static void linenoiseBeep(void) { - fprintf(stderr, "\x7"); - fflush(stderr); + fprintf(stderr, "\x7"); + fflush(stderr); } /* ============================== Completion ================================ */ /* Free a list of completion option populated by linenoiseAddCompletion(). */ static void freeCompletions(linenoiseCompletions *lc) { - size_t i; - for (i = 0; i < lc->len; i++) - free(lc->cvec[i]); - if (lc->cvec != NULL) - free(lc->cvec); + size_t i; + for (i = 0; i < lc->len; i++) + free(lc->cvec[i]); + if (lc->cvec != NULL) + free(lc->cvec); } /* This is an helper function for linenoiseEdit() and is called when the @@ -362,78 +362,78 @@ static void freeCompletions(linenoiseCompletions *lc) { * The state of the editing is encapsulated into the pointed linenoiseState * structure as described in the structure definition. */ static int completeLine(struct linenoiseState *ls) { - linenoiseCompletions lc = { 0, NULL }; - int nread, nwritten; - char c = 0; + linenoiseCompletions lc = { 0, NULL }; + int nread, nwritten; + char c = 0; - completionCallback(ls->buf,&lc); - if (lc.len == 0) { - linenoiseBeep(); - } else { - size_t stop = 0, i = 0; + completionCallback(ls->buf,&lc); + if (lc.len == 0) { + linenoiseBeep(); + } else { + size_t stop = 0, i = 0; - while(!stop) { - /* Show completion or original buffer */ - if (i < lc.len) { - struct linenoiseState saved = *ls; + while(!stop) { + /* Show completion or original buffer */ + if (i < lc.len) { + struct linenoiseState saved = *ls; - ls->len = ls->pos = strlen(lc.cvec[i]); - ls->buf = lc.cvec[i]; - refreshLine(ls); - ls->len = saved.len; - ls->pos = saved.pos; - ls->buf = saved.buf; - } else { - refreshLine(ls); - } + ls->len = ls->pos = strlen(lc.cvec[i]); + ls->buf = lc.cvec[i]; + refreshLine(ls); + ls->len = saved.len; + ls->pos = saved.pos; + ls->buf = saved.buf; + } else { + refreshLine(ls); + } - nread = read(ls->ifd,&c,1); - if (nread <= 0) { - freeCompletions(&lc); - return -1; - } + nread = read(ls->ifd,&c,1); + if (nread <= 0) { + freeCompletions(&lc); + return -1; + } - switch(c) { - case 9: /* tab */ - i = (i+1) % (lc.len+1); - if (i == lc.len) linenoiseBeep(); - break; - case 27: /* escape */ - /* Re-show original buffer */ - if (i < lc.len) refreshLine(ls); - stop = 1; - break; - default: - /* Update buffer and return */ - if (i < lc.len) { - nwritten = snprintf(ls->buf,ls->buflen,"%s",lc.cvec[i]); - ls->len = ls->pos = nwritten; - } - stop = 1; - break; - } - } - } + switch(c) { + case 9: /* tab */ + i = (i+1) % (lc.len+1); + if (i == lc.len) linenoiseBeep(); + break; + case 27: /* escape */ + /* Re-show original buffer */ + if (i < lc.len) refreshLine(ls); + stop = 1; + break; + default: + /* Update buffer and return */ + if (i < lc.len) { + nwritten = snprintf(ls->buf,ls->buflen,"%s",lc.cvec[i]); + ls->len = ls->pos = nwritten; + } + stop = 1; + break; + } + } + } - freeCompletions(&lc); - return c; /* Return last read character */ + freeCompletions(&lc); + return c; /* Return last read character */ } /* Register a callback function to be called for tab-completion. */ void linenoiseSetCompletionCallback(linenoiseCompletionCallback *fn) { - completionCallback = fn; + completionCallback = fn; } /* Register a hits function to be called to show hits to the user at the * right of the prompt. */ void linenoiseSetHintsCallback(linenoiseHintsCallback *fn) { - hintsCallback = fn; + hintsCallback = fn; } /* Register a function to free the hints returned by the hints callback * registered with linenoiseSetHintsCallback(). */ void linenoiseSetFreeHintsCallback(linenoiseFreeHintsCallback *fn) { - freeHintsCallback = fn; + freeHintsCallback = fn; } /* This function is used by the callback function registered by the user @@ -441,19 +441,19 @@ void linenoiseSetFreeHintsCallback(linenoiseFreeHintsCallback *fn) { * user typed . See the example.c source code for a very easy to * understand example. */ void linenoiseAddCompletion(linenoiseCompletions *lc, const char *str) { - size_t len = strlen(str); - char *copy, **cvec; + size_t len = strlen(str); + char *copy, **cvec; - copy = malloc(len+1); - if (copy == NULL) return; - memcpy(copy,str,len+1); - cvec = realloc(lc->cvec,sizeof(char*)*(lc->len+1)); - if (cvec == NULL) { - free(copy); - return; - } - lc->cvec = cvec; - lc->cvec[lc->len++] = copy; + copy = malloc(len+1); + if (copy == NULL) return; + memcpy(copy,str,len+1); + cvec = realloc(lc->cvec,sizeof(char*)*(lc->len+1)); + if (cvec == NULL) { + free(copy); + return; + } + lc->cvec = cvec; + lc->cvec[lc->len++] = copy; } /* =========================== Line editing ================================= */ @@ -468,47 +468,47 @@ struct abuf { }; static void abInit(struct abuf *ab) { - ab->b = NULL; - ab->len = 0; + ab->b = NULL; + ab->len = 0; } static void abAppend(struct abuf *ab, const char *s, int len) { - char *new = realloc(ab->b,ab->len+len); + char *new = realloc(ab->b,ab->len+len); - if (new == NULL) return; - memcpy(new+ab->len,s,len); - ab->b = new; - ab->len += len; + if (new == NULL) return; + memcpy(new+ab->len,s,len); + ab->b = new; + ab->len += len; } static void abFree(struct abuf *ab) { - free(ab->b); + free(ab->b); } /* Helper of refreshSingleLine() and refreshMultiLine() to show hints * to the right of the prompt. */ void refreshShowHints(struct abuf *ab, struct linenoiseState *l, int plen) { - char seq[64]; - if (hintsCallback && plen+l->len < l->cols) { - int color = -1, bold = 0; - char *hint = hintsCallback(l->buf,&color,&bold); - if (hint) { - int hintlen = strlen(hint); - int hintmaxlen = l->cols-(plen+l->len); - if (hintlen > hintmaxlen) hintlen = hintmaxlen; - if (bold == 1 && color == -1) color = 37; - if (color != -1 || bold != 0) - snprintf(seq,64,"\033[%d;%d;49m",bold,color); - else - seq[0] = '\0'; - abAppend(ab,seq,strlen(seq)); - abAppend(ab,hint,hintlen); - if (color != -1 || bold != 0) - abAppend(ab,"\033[0m",4); - /* Call the function to free the hint returned. */ - if (freeHintsCallback) freeHintsCallback(hint); - } - } + char seq[64]; + if (hintsCallback && plen+l->len < l->cols) { + int color = -1, bold = 0; + char *hint = hintsCallback(l->buf,&color,&bold); + if (hint) { + int hintlen = strlen(hint); + int hintmaxlen = l->cols-(plen+l->len); + if (hintlen > hintmaxlen) hintlen = hintmaxlen; + if (bold == 1 && color == -1) color = 37; + if (color != -1 || bold != 0) + snprintf(seq,64,"\033[%d;%d;49m",bold,color); + else + seq[0] = '\0'; + abAppend(ab,seq,strlen(seq)); + abAppend(ab,hint,hintlen); + if (color != -1 || bold != 0) + abAppend(ab,"\033[0m",4); + /* Call the function to free the hint returned. */ + if (freeHintsCallback) freeHintsCallback(hint); + } + } } /* Single line low level line refresh. @@ -516,44 +516,44 @@ void refreshShowHints(struct abuf *ab, struct linenoiseState *l, int plen) { * Rewrite the currently edited line accordingly to the buffer content, * cursor position, and number of columns of the terminal. */ static void refreshSingleLine(struct linenoiseState *l) { - char seq[64]; - size_t plen = strlen(l->prompt); - int fd = l->ofd; - char *buf = l->buf; - size_t len = l->len; - size_t pos = l->pos; - struct abuf ab; + char seq[64]; + size_t plen = strlen(l->prompt); + int fd = l->ofd; + char *buf = l->buf; + size_t len = l->len; + size_t pos = l->pos; + struct abuf ab; - while((plen+pos) >= l->cols) { - buf++; - len--; - pos--; - } - while (plen+len > l->cols) { - len--; - } + while((plen+pos) >= l->cols) { + buf++; + len--; + pos--; + } + while (plen+len > l->cols) { + len--; + } - abInit(&ab); - /* Cursor to left edge */ - snprintf(seq,64,"\r"); - abAppend(&ab,seq,strlen(seq)); - /* Write the prompt and the current buffer content */ - abAppend(&ab,l->prompt,strlen(l->prompt)); - if (maskmode == 1) { - while (len--) abAppend(&ab,"*",1); - } else { - abAppend(&ab,buf,len); - } - /* Show hits if any. */ - refreshShowHints(&ab,l,plen); - /* Erase to right */ - snprintf(seq,64,"\x1b[0K"); - abAppend(&ab,seq,strlen(seq)); - /* Move cursor to original position. */ - snprintf(seq,64,"\r\x1b[%dC", (int)(pos+plen)); - abAppend(&ab,seq,strlen(seq)); - if (write(fd,ab.b,ab.len) == -1) {} /* Can't recover from write error. */ - abFree(&ab); + abInit(&ab); + /* Cursor to left edge */ + snprintf(seq,64,"\r"); + abAppend(&ab,seq,strlen(seq)); + /* Write the prompt and the current buffer content */ + abAppend(&ab,l->prompt,strlen(l->prompt)); + if (maskmode == 1) { + while (len--) abAppend(&ab,"*",1); + } else { + abAppend(&ab,buf,len); + } + /* Show hits if any. */ + refreshShowHints(&ab,l,plen); + /* Erase to right */ + snprintf(seq,64,"\x1b[0K"); + abAppend(&ab,seq,strlen(seq)); + /* Move cursor to original position. */ + snprintf(seq,64,"\r\x1b[%dC", (int)(pos+plen)); + abAppend(&ab,seq,strlen(seq)); + if (write(fd,ab.b,ab.len) == -1) {} /* Can't recover from write error. */ + abFree(&ab); } /* Multi line low level line refresh. @@ -561,179 +561,162 @@ static void refreshSingleLine(struct linenoiseState *l) { * Rewrite the currently edited line accordingly to the buffer content, * cursor position, and number of columns of the terminal. */ static void refreshMultiLine(struct linenoiseState *l) { - char seq[64]; - int plen = strlen(l->prompt); - int rows = (plen+l->len+l->cols-1)/l->cols; /* rows used by current buf. */ - int rpos = (plen+l->oldpos+l->cols)/l->cols; /* cursor relative row. */ - int rpos2; /* rpos after refresh. */ - int col; /* colum position, zero-based. */ - int old_rows = l->maxrows; - int fd = l->ofd, j; - struct abuf ab; + char seq[64]; + int plen = strlen(l->prompt); + int rows = (plen+l->len+l->cols-1)/l->cols; /* rows used by current buf. */ + int rpos = (plen+l->oldpos+l->cols)/l->cols; /* cursor relative row. */ + int rpos2; /* rpos after refresh. */ + int col; /* colum position, zero-based. */ + int old_rows = l->maxrows; + int fd = l->ofd, j; + struct abuf ab; - /* Update maxrows if needed. */ - if (rows > (int)l->maxrows) l->maxrows = rows; + /* Update maxrows if needed. */ + if (rows > (int)l->maxrows) l->maxrows = rows; - /* First step: clear all the lines used before. To do so start by - * going to the last row. */ - abInit(&ab); - if (old_rows-rpos > 0) { - lndebug("go down %d", old_rows-rpos); - snprintf(seq,64,"\x1b[%dB", old_rows-rpos); - abAppend(&ab,seq,strlen(seq)); - } + /* First step: clear all the lines used before. To do so start by + * going to the last row. */ + abInit(&ab); + if (old_rows-rpos > 0) { + lndebug("go down %d", old_rows-rpos); + snprintf(seq,64,"\x1b[%dB", old_rows-rpos); + abAppend(&ab,seq,strlen(seq)); + } - /* Now for every row clear it, go up. */ - for (j = 0; j < old_rows-1; j++) { - lndebug("clear+up"); - snprintf(seq,64,"\r\x1b[0K\x1b[1A"); - abAppend(&ab,seq,strlen(seq)); - } + /* Now for every row clear it, go up. */ + for (j = 0; j < old_rows-1; j++) { + lndebug("clear+up"); + snprintf(seq,64,"\r\x1b[0K\x1b[1A"); + abAppend(&ab,seq,strlen(seq)); + } - /* Clean the top line. */ - lndebug("clear"); - snprintf(seq,64,"\r\x1b[0K"); - abAppend(&ab,seq,strlen(seq)); + /* Clean the top line. */ + lndebug("clear"); + snprintf(seq,64,"\r\x1b[0K"); + abAppend(&ab,seq,strlen(seq)); - /* Write the prompt and the current buffer content */ - abAppend(&ab,l->prompt,strlen(l->prompt)); - if (maskmode == 1) { - unsigned int i; - for (i = 0; i < l->len; i++) abAppend(&ab,"*",1); - } else { - int colors[] = {31, 32, 33, 35, 36}; - int nesting = 0; - int incode = 1; /* not in literal; TODO add in comment */ - for (int i = 0; i < l->len; i++) { - if (l->buf[i] == '"' && !(i > 0 && l->buf[i-1] == '\\')) - incode = (incode == 1 ? 0 : 1); - if (incode == 1 && l->buf[i] == '(') { - snprintf(seq,64,"\033[%d;1m(\033[0m",colors[nesting % (sizeof(colors)/sizeof(colors[0]))]); - abAppend(&ab,seq,strlen(seq)); - nesting++; - } else if (incode == 1 && l->buf[i] == ')') { - nesting--; - snprintf(seq,64,"\033[%d;1m)\033[0m",colors[nesting % (sizeof(colors)/sizeof(colors[0]))]); - abAppend(&ab,seq,strlen(seq)); - } else { - abAppend(&ab,l->buf+i,1); - } - } - } + /* Write the prompt and the current buffer content */ + abAppend(&ab,l->prompt,strlen(l->prompt)); + if (maskmode == 1) { + unsigned int i; + for (i = 0; i < l->len; i++) abAppend(&ab,"*",1); + } else { + abAppend(&ab,l->buf,l->len); + } - /* Show hits if any. */ - refreshShowHints(&ab,l,plen); + /* Show hits if any. */ + refreshShowHints(&ab,l,plen); - /* If we are at the very end of the screen with our prompt, we need to - * emit a newline and move the prompt to the first column. */ - if (l->pos && - l->pos == l->len && - (l->pos+plen) % l->cols == 0) - { - lndebug(""); - abAppend(&ab,"\n",1); - snprintf(seq,64,"\r"); - abAppend(&ab,seq,strlen(seq)); - rows++; - if (rows > (int)l->maxrows) l->maxrows = rows; - } + /* If we are at the very end of the screen with our prompt, we need to + * emit a newline and move the prompt to the first column. */ + if (l->pos && + l->pos == l->len && + (l->pos+plen) % l->cols == 0) + { + lndebug(""); + abAppend(&ab,"\n",1); + snprintf(seq,64,"\r"); + abAppend(&ab,seq,strlen(seq)); + rows++; + if (rows > (int)l->maxrows) l->maxrows = rows; + } - /* Move cursor to right position. */ - rpos2 = (plen+l->pos+l->cols)/l->cols; /* current cursor relative row. */ - lndebug("rpos2 %d", rpos2); + /* Move cursor to right position. */ + rpos2 = (plen+l->pos+l->cols)/l->cols; /* current cursor relative row. */ + lndebug("rpos2 %d", rpos2); - /* Go up till we reach the expected positon. */ - if (rows-rpos2 > 0) { - lndebug("go-up %d", rows-rpos2); - snprintf(seq,64,"\x1b[%dA", rows-rpos2); - abAppend(&ab,seq,strlen(seq)); - } + /* Go up till we reach the expected positon. */ + if (rows-rpos2 > 0) { + lndebug("go-up %d", rows-rpos2); + snprintf(seq,64,"\x1b[%dA", rows-rpos2); + abAppend(&ab,seq,strlen(seq)); + } - /* Set column. */ - col = (plen+(int)l->pos) % (int)l->cols; - lndebug("set col %d", 1+col); - if (col) - snprintf(seq,64,"\r\x1b[%dC", col); - else - snprintf(seq,64,"\r"); - abAppend(&ab,seq,strlen(seq)); + /* Set column. */ + col = (plen+(int)l->pos) % (int)l->cols; + lndebug("set col %d", 1+col); + if (col) + snprintf(seq,64,"\r\x1b[%dC", col); + else + snprintf(seq,64,"\r"); + abAppend(&ab,seq,strlen(seq)); - lndebug("\n"); - l->oldpos = l->pos; + lndebug("\n"); + l->oldpos = l->pos; - if (write(fd,ab.b,ab.len) == -1) {} /* Can't recover from write error. */ - abFree(&ab); + if (write(fd,ab.b,ab.len) == -1) {} /* Can't recover from write error. */ + abFree(&ab); } /* Calls the two low level functions refreshSingleLine() or * refreshMultiLine() according to the selected mode. */ static void refreshLine(struct linenoiseState *l) { - if (mlmode) - refreshMultiLine(l); - else - refreshSingleLine(l); + if (mlmode) + refreshMultiLine(l); + else + refreshSingleLine(l); } /* Insert the character 'c' at cursor current position. * * On error writing to the terminal -1 is returned, otherwise 0. */ int linenoiseEditInsert(struct linenoiseState *l, char c) { - if (l->len < l->buflen) { - if (l->len == l->pos) { - l->buf[l->pos] = c; - l->pos++; - l->len++; - l->buf[l->len] = '\0'; - if ((!mlmode && l->plen+l->len < l->cols && !hintsCallback)) { - /* Avoid a full update of the line in the - * trivial case. */ - char d = (maskmode==1) ? '*' : c; - if (write(l->ofd,&d,1) == -1) return -1; - } else { - refreshLine(l); - } - } else { - memmove(l->buf+l->pos+1,l->buf+l->pos,l->len-l->pos); - l->buf[l->pos] = c; - l->len++; - l->pos++; - l->buf[l->len] = '\0'; - refreshLine(l); - } - } - return 0; + if (l->len < l->buflen) { + if (l->len == l->pos) { + l->buf[l->pos] = c; + l->pos++; + l->len++; + l->buf[l->len] = '\0'; + if ((!mlmode && l->plen+l->len < l->cols && !hintsCallback)) { + /* Avoid a full update of the line in the + * trivial case. */ + char d = (maskmode==1) ? '*' : c; + if (write(l->ofd,&d,1) == -1) return -1; + } else { + refreshLine(l); + } + } else { + memmove(l->buf+l->pos+1,l->buf+l->pos,l->len-l->pos); + l->buf[l->pos] = c; + l->len++; + l->pos++; + l->buf[l->len] = '\0'; + refreshLine(l); + } + } + return 0; } /* Move cursor on the left. */ void linenoiseEditMoveLeft(struct linenoiseState *l) { - if (l->pos > 0) { - l->pos--; - refreshLine(l); - } + if (l->pos > 0) { + l->pos--; + refreshLine(l); + } } /* Move cursor on the right. */ void linenoiseEditMoveRight(struct linenoiseState *l) { - if (l->pos != l->len) { - l->pos++; - refreshLine(l); - } + if (l->pos != l->len) { + l->pos++; + refreshLine(l); + } } /* Move cursor to the start of the line. */ void linenoiseEditMoveHome(struct linenoiseState *l) { - if (l->pos != 0) { - l->pos = 0; - refreshLine(l); - } + if (l->pos != 0) { + l->pos = 0; + refreshLine(l); + } } /* Move cursor to the end of the line. */ void linenoiseEditMoveEnd(struct linenoiseState *l) { - if (l->pos != l->len) { - l->pos = l->len; - refreshLine(l); - } + if (l->pos != l->len) { + l->pos = l->len; + refreshLine(l); + } } /* Substitute the currently edited line with the next or previous history @@ -741,63 +724,63 @@ void linenoiseEditMoveEnd(struct linenoiseState *l) { #define LINENOISE_HISTORY_NEXT 0 #define LINENOISE_HISTORY_PREV 1 void linenoiseEditHistoryNext(struct linenoiseState *l, int dir) { - if (history_len > 1) { - /* Update the current history entry before to - * overwrite it with the next one. */ - free(history[history_len - 1 - l->history_index]); - history[history_len - 1 - l->history_index] = strdup(l->buf); - /* Show the new entry */ - l->history_index += (dir == LINENOISE_HISTORY_PREV) ? 1 : -1; - if (l->history_index < 0) { - l->history_index = 0; - return; - } else if (l->history_index >= history_len) { - l->history_index = history_len-1; - return; - } - strncpy(l->buf,history[history_len - 1 - l->history_index],l->buflen); - l->buf[l->buflen-1] = '\0'; - l->len = l->pos = strlen(l->buf); - refreshLine(l); - } + if (history_len > 1) { + /* Update the current history entry before to + * overwrite it with the next one. */ + free(history[history_len - 1 - l->history_index]); + history[history_len - 1 - l->history_index] = strdup(l->buf); + /* Show the new entry */ + l->history_index += (dir == LINENOISE_HISTORY_PREV) ? 1 : -1; + if (l->history_index < 0) { + l->history_index = 0; + return; + } else if (l->history_index >= history_len) { + l->history_index = history_len-1; + return; + } + strncpy(l->buf,history[history_len - 1 - l->history_index],l->buflen); + l->buf[l->buflen-1] = '\0'; + l->len = l->pos = strlen(l->buf); + refreshLine(l); + } } /* Delete the character at the right of the cursor without altering the cursor * position. Basically this is what happens with the "Delete" keyboard key. */ void linenoiseEditDelete(struct linenoiseState *l) { - if (l->len > 0 && l->pos < l->len) { - memmove(l->buf+l->pos,l->buf+l->pos+1,l->len-l->pos-1); - l->len--; - l->buf[l->len] = '\0'; - refreshLine(l); - } + if (l->len > 0 && l->pos < l->len) { + memmove(l->buf+l->pos,l->buf+l->pos+1,l->len-l->pos-1); + l->len--; + l->buf[l->len] = '\0'; + refreshLine(l); + } } /* Backspace implementation. */ void linenoiseEditBackspace(struct linenoiseState *l) { - if (l->pos > 0 && l->len > 0) { - memmove(l->buf+l->pos-1,l->buf+l->pos,l->len-l->pos); - l->pos--; - l->len--; - l->buf[l->len] = '\0'; - refreshLine(l); - } + if (l->pos > 0 && l->len > 0) { + memmove(l->buf+l->pos-1,l->buf+l->pos,l->len-l->pos); + l->pos--; + l->len--; + l->buf[l->len] = '\0'; + refreshLine(l); + } } /* Delete the previosu word, maintaining the cursor at the start of the * current word. */ void linenoiseEditDeletePrevWord(struct linenoiseState *l) { - size_t old_pos = l->pos; - size_t diff; + size_t old_pos = l->pos; + size_t diff; - while (l->pos > 0 && l->buf[l->pos-1] == ' ') - l->pos--; - while (l->pos > 0 && l->buf[l->pos-1] != ' ') - l->pos--; - diff = old_pos - l->pos; - memmove(l->buf+l->pos,l->buf+old_pos,l->len-old_pos+1); - l->len -= diff; - refreshLine(l); + while (l->pos > 0 && l->buf[l->pos-1] == ' ') + l->pos--; + while (l->pos > 0 && l->buf[l->pos-1] != ' ') + l->pos--; + diff = old_pos - l->pos; + memmove(l->buf+l->pos,l->buf+old_pos,l->len-old_pos+1); + l->len -= diff; + refreshLine(l); } /* This function is the core of the line editing capability of linenoise. @@ -810,231 +793,231 @@ void linenoiseEditDeletePrevWord(struct linenoiseState *l) { * The function returns the length of the current buffer. */ static int linenoiseEdit(int stdin_fd, int stdout_fd, char *buf, size_t buflen, const char *prompt) { - struct linenoiseState l; + struct linenoiseState l; - /* Populate the linenoise state that we pass to functions implementing - * specific editing functionalities. */ - l.ifd = stdin_fd; - l.ofd = stdout_fd; - l.buf = buf; - l.buflen = buflen; - l.prompt = prompt; - l.plen = strlen(prompt); - l.oldpos = l.pos = 0; - l.len = 0; - l.cols = getColumns(stdin_fd, stdout_fd); - l.maxrows = 0; - l.history_index = 0; + /* Populate the linenoise state that we pass to functions implementing + * specific editing functionalities. */ + l.ifd = stdin_fd; + l.ofd = stdout_fd; + l.buf = buf; + l.buflen = buflen; + l.prompt = prompt; + l.plen = strlen(prompt); + l.oldpos = l.pos = 0; + l.len = 0; + l.cols = getColumns(stdin_fd, stdout_fd); + l.maxrows = 0; + l.history_index = 0; - /* Buffer starts empty. */ - l.buf[0] = '\0'; - l.buflen--; /* Make sure there is always space for the nulterm */ + /* Buffer starts empty. */ + l.buf[0] = '\0'; + l.buflen--; /* Make sure there is always space for the nulterm */ - /* The latest history entry is always our current buffer, that - * initially is just an empty string. */ - linenoiseHistoryAdd(""); + /* The latest history entry is always our current buffer, that + * initially is just an empty string. */ + linenoiseHistoryAdd(""); - if (write(l.ofd,prompt,l.plen) == -1) return -1; - while(1) { - char c; - int nread; - char seq[3]; + if (write(l.ofd,prompt,l.plen) == -1) return -1; + while(1) { + char c; + int nread; + char seq[3]; - nread = read(l.ifd,&c,1); - if (nread <= 0) return l.len; + nread = read(l.ifd,&c,1); + if (nread <= 0) return l.len; - /* Only autocomplete when the callback is set. It returns < 0 when - * there was an error reading from fd. Otherwise it will return the - * character that should be handled next. */ - if (c == 9 && completionCallback != NULL) { - c = completeLine(&l); - /* Return on errors */ - if (c < 0) return l.len; - /* Read next character when 0 */ - if (c == 0) continue; - } + /* Only autocomplete when the callback is set. It returns < 0 when + * there was an error reading from fd. Otherwise it will return the + * character that should be handled next. */ + if (c == 9 && completionCallback != NULL) { + c = completeLine(&l); + /* Return on errors */ + if (c < 0) return l.len; + /* Read next character when 0 */ + if (c == 0) continue; + } - switch(c) { - case ENTER: /* enter */ - history_len--; - free(history[history_len]); - if (mlmode) linenoiseEditMoveEnd(&l); - if (hintsCallback) { - /* Force a refresh without hints to leave the previous - * line as the user typed it after a newline. */ - linenoiseHintsCallback *hc = hintsCallback; - hintsCallback = NULL; - refreshLine(&l); - hintsCallback = hc; - } - return (int)l.len; - case CTRL_C: /* ctrl-c */ - errno = EAGAIN; - return -1; - case BACKSPACE: /* backspace */ - case 8: /* ctrl-h */ - linenoiseEditBackspace(&l); - break; - case CTRL_D: /* ctrl-d, remove char at right of cursor, or if the + switch(c) { + case ENTER: /* enter */ + history_len--; + free(history[history_len]); + if (mlmode) linenoiseEditMoveEnd(&l); + if (hintsCallback) { + /* Force a refresh without hints to leave the previous + * line as the user typed it after a newline. */ + linenoiseHintsCallback *hc = hintsCallback; + hintsCallback = NULL; + refreshLine(&l); + hintsCallback = hc; + } + return (int)l.len; + case CTRL_C: /* ctrl-c */ + errno = EAGAIN; + return -1; + case BACKSPACE: /* backspace */ + case 8: /* ctrl-h */ + linenoiseEditBackspace(&l); + break; + case CTRL_D: /* ctrl-d, remove char at right of cursor, or if the line is empty, act as end-of-file. */ - if (l.len > 0) { - linenoiseEditDelete(&l); - } else { - history_len--; - free(history[history_len]); - return -1; - } - break; - case CTRL_T: /* ctrl-t, swaps current character with previous. */ - if (l.pos > 0 && l.pos < l.len) { - int aux = buf[l.pos-1]; - buf[l.pos-1] = buf[l.pos]; - buf[l.pos] = aux; - if (l.pos != l.len-1) l.pos++; - refreshLine(&l); - } - break; - case CTRL_B: /* ctrl-b */ - linenoiseEditMoveLeft(&l); - break; - case CTRL_F: /* ctrl-f */ - linenoiseEditMoveRight(&l); - break; - case CTRL_P: /* ctrl-p */ - linenoiseEditHistoryNext(&l, LINENOISE_HISTORY_PREV); - break; - case CTRL_N: /* ctrl-n */ - linenoiseEditHistoryNext(&l, LINENOISE_HISTORY_NEXT); - break; - case ESC: /* escape sequence */ - /* Read the next two bytes representing the escape sequence. - * Use two calls to handle slow terminals returning the two - * chars at different times. */ - if (read(l.ifd,seq,1) == -1) break; - if (read(l.ifd,seq+1,1) == -1) break; + if (l.len > 0) { + linenoiseEditDelete(&l); + } else { + history_len--; + free(history[history_len]); + return -1; + } + break; + case CTRL_T: /* ctrl-t, swaps current character with previous. */ + if (l.pos > 0 && l.pos < l.len) { + int aux = buf[l.pos-1]; + buf[l.pos-1] = buf[l.pos]; + buf[l.pos] = aux; + if (l.pos != l.len-1) l.pos++; + refreshLine(&l); + } + break; + case CTRL_B: /* ctrl-b */ + linenoiseEditMoveLeft(&l); + break; + case CTRL_F: /* ctrl-f */ + linenoiseEditMoveRight(&l); + break; + case CTRL_P: /* ctrl-p */ + linenoiseEditHistoryNext(&l, LINENOISE_HISTORY_PREV); + break; + case CTRL_N: /* ctrl-n */ + linenoiseEditHistoryNext(&l, LINENOISE_HISTORY_NEXT); + break; + case ESC: /* escape sequence */ + /* Read the next two bytes representing the escape sequence. + * Use two calls to handle slow terminals returning the two + * chars at different times. */ + if (read(l.ifd,seq,1) == -1) break; + if (read(l.ifd,seq+1,1) == -1) break; - /* ESC [ sequences. */ - if (seq[0] == '[') { - if (seq[1] >= '0' && seq[1] <= '9') { - /* Extended escape, read additional byte. */ - if (read(l.ifd,seq+2,1) == -1) break; - if (seq[2] == '~') { - switch(seq[1]) { - case '3': /* Delete key. */ - linenoiseEditDelete(&l); - break; - } - } - } else { - switch(seq[1]) { - case 'A': /* Up */ - linenoiseEditHistoryNext(&l, LINENOISE_HISTORY_PREV); - break; - case 'B': /* Down */ - linenoiseEditHistoryNext(&l, LINENOISE_HISTORY_NEXT); - break; - case 'C': /* Right */ - linenoiseEditMoveRight(&l); - break; - case 'D': /* Left */ - linenoiseEditMoveLeft(&l); - break; - case 'H': /* Home */ - linenoiseEditMoveHome(&l); - break; - case 'F': /* End*/ - linenoiseEditMoveEnd(&l); - break; - } - } - } + /* ESC [ sequences. */ + if (seq[0] == '[') { + if (seq[1] >= '0' && seq[1] <= '9') { + /* Extended escape, read additional byte. */ + if (read(l.ifd,seq+2,1) == -1) break; + if (seq[2] == '~') { + switch(seq[1]) { + case '3': /* Delete key. */ + linenoiseEditDelete(&l); + break; + } + } + } else { + switch(seq[1]) { + case 'A': /* Up */ + linenoiseEditHistoryNext(&l, LINENOISE_HISTORY_PREV); + break; + case 'B': /* Down */ + linenoiseEditHistoryNext(&l, LINENOISE_HISTORY_NEXT); + break; + case 'C': /* Right */ + linenoiseEditMoveRight(&l); + break; + case 'D': /* Left */ + linenoiseEditMoveLeft(&l); + break; + case 'H': /* Home */ + linenoiseEditMoveHome(&l); + break; + case 'F': /* End*/ + linenoiseEditMoveEnd(&l); + break; + } + } + } - /* ESC O sequences. */ - else if (seq[0] == 'O') { - switch(seq[1]) { - case 'H': /* Home */ - linenoiseEditMoveHome(&l); - break; - case 'F': /* End*/ - linenoiseEditMoveEnd(&l); - break; - } - } - break; - default: - if (linenoiseEditInsert(&l,c)) return -1; - break; - case CTRL_U: /* Ctrl+u, delete the whole line. */ - buf[0] = '\0'; - l.pos = l.len = 0; - refreshLine(&l); - break; - case CTRL_K: /* Ctrl+k, delete from current to end of line. */ - buf[l.pos] = '\0'; - l.len = l.pos; - refreshLine(&l); - break; - case CTRL_A: /* Ctrl+a, go to the start of the line */ - linenoiseEditMoveHome(&l); - break; - case CTRL_E: /* ctrl+e, go to the end of the line */ - linenoiseEditMoveEnd(&l); - break; - case CTRL_L: /* ctrl+l, clear screen */ - linenoiseClearScreen(); - refreshLine(&l); - break; - case CTRL_W: /* ctrl+w, delete previous word */ - linenoiseEditDeletePrevWord(&l); - break; - } - } - return l.len; + /* ESC O sequences. */ + else if (seq[0] == 'O') { + switch(seq[1]) { + case 'H': /* Home */ + linenoiseEditMoveHome(&l); + break; + case 'F': /* End*/ + linenoiseEditMoveEnd(&l); + break; + } + } + break; + default: + if (linenoiseEditInsert(&l,c)) return -1; + break; + case CTRL_U: /* Ctrl+u, delete the whole line. */ + buf[0] = '\0'; + l.pos = l.len = 0; + refreshLine(&l); + break; + case CTRL_K: /* Ctrl+k, delete from current to end of line. */ + buf[l.pos] = '\0'; + l.len = l.pos; + refreshLine(&l); + break; + case CTRL_A: /* Ctrl+a, go to the start of the line */ + linenoiseEditMoveHome(&l); + break; + case CTRL_E: /* ctrl+e, go to the end of the line */ + linenoiseEditMoveEnd(&l); + break; + case CTRL_L: /* ctrl+l, clear screen */ + linenoiseClearScreen(); + refreshLine(&l); + break; + case CTRL_W: /* ctrl+w, delete previous word */ + linenoiseEditDeletePrevWord(&l); + break; + } + } + return l.len; } /* This special mode is used by linenoise in order to print scan codes * on screen for debugging / development purposes. It is implemented * by the linenoise_example program using the --keycodes option. */ void linenoisePrintKeyCodes(void) { - char quit[4]; + char quit[4]; - printf("Linenoise key codes debugging mode.\n" - "Press keys to see scan codes. Type 'quit' at any time to exit.\n"); - if (enableRawMode(STDIN_FILENO) == -1) return; - memset(quit,' ',4); - while(1) { - char c; - int nread; + printf("Linenoise key codes debugging mode.\n" + "Press keys to see scan codes. Type 'quit' at any time to exit.\n"); + if (enableRawMode(STDIN_FILENO) == -1) return; + memset(quit,' ',4); + while(1) { + char c; + int nread; - nread = read(STDIN_FILENO,&c,1); - if (nread <= 0) continue; - memmove(quit,quit+1,sizeof(quit)-1); /* shift string to left. */ - quit[sizeof(quit)-1] = c; /* Insert current char on the right. */ - if (memcmp(quit,"quit",sizeof(quit)) == 0) break; + nread = read(STDIN_FILENO,&c,1); + if (nread <= 0) continue; + memmove(quit,quit+1,sizeof(quit)-1); /* shift string to left. */ + quit[sizeof(quit)-1] = c; /* Insert current char on the right. */ + if (memcmp(quit,"quit",sizeof(quit)) == 0) break; - printf("'%c' %02x (%d) (type quit to exit)\n", - isprint(c) ? c : '?', (int)c, (int)c); - printf("\r"); /* Go left edge manually, we are in raw mode. */ - fflush(stdout); - } - disableRawMode(STDIN_FILENO); + printf("'%c' %02x (%d) (type quit to exit)\n", + isprint(c) ? c : '?', (int)c, (int)c); + printf("\r"); /* Go left edge manually, we are in raw mode. */ + fflush(stdout); + } + disableRawMode(STDIN_FILENO); } /* This function calls the line editing function linenoiseEdit() using * the STDIN file descriptor set in raw mode. */ static int linenoiseRaw(char *buf, size_t buflen, const char *prompt) { - int count; + int count; - if (buflen == 0) { - errno = EINVAL; - return -1; - } + if (buflen == 0) { + errno = EINVAL; + return -1; + } - if (enableRawMode(STDIN_FILENO) == -1) return -1; - count = linenoiseEdit(STDIN_FILENO, STDOUT_FILENO, buf, buflen, prompt); - disableRawMode(STDIN_FILENO); - printf("\n"); - return count; + if (enableRawMode(STDIN_FILENO) == -1) return -1; + count = linenoiseEdit(STDIN_FILENO, STDOUT_FILENO, buf, buflen, prompt); + disableRawMode(STDIN_FILENO); + printf("\n"); + return count; } /* This function is called when linenoise() is called with the standard @@ -1043,34 +1026,34 @@ static int linenoiseRaw(char *buf, size_t buflen, const char *prompt) { * to its standard input. In this case, we want to be able to return the * line regardless of its length (by default we are limited to 4k). */ static char *linenoiseNoTTY(void) { - char *line = NULL; - size_t len = 0, maxlen = 0; + char *line = NULL; + size_t len = 0, maxlen = 0; - while(1) { - if (len == maxlen) { - if (maxlen == 0) maxlen = 16; - maxlen *= 2; - char *oldval = line; - line = realloc(line,maxlen); - if (line == NULL) { - if (oldval) free(oldval); - return NULL; - } - } - int c = fgetc(stdin); - if (c == EOF || c == '\n') { - if (c == EOF && len == 0) { - free(line); - return NULL; - } else { - line[len] = '\0'; - return line; - } - } else { - line[len] = c; - len++; - } - } + while(1) { + if (len == maxlen) { + if (maxlen == 0) maxlen = 16; + maxlen *= 2; + char *oldval = line; + line = realloc(line,maxlen); + if (line == NULL) { + if (oldval) free(oldval); + return NULL; + } + } + int c = fgetc(stdin); + if (c == EOF || c == '\n') { + if (c == EOF && len == 0) { + free(line); + return NULL; + } else { + line[len] = '\0'; + return line; + } + } else { + line[len] = c; + len++; + } + } } /* The high level function that is the main API of the linenoise library. @@ -1079,30 +1062,30 @@ static char *linenoiseNoTTY(void) { * editing function or uses dummy fgets() so that you will be able to type * something even in the most desperate of the conditions. */ char *linenoise(const char *prompt) { - char buf[LINENOISE_MAX_LINE]; - int count; + char buf[LINENOISE_MAX_LINE]; + int count; - if (!isatty(STDIN_FILENO)) { - /* Not a tty: read from file / pipe. In this mode we don't want any - * limit to the line size, so we call a function to handle that. */ - return linenoiseNoTTY(); - } else if (isUnsupportedTerm()) { - size_t len; + if (!isatty(STDIN_FILENO)) { + /* Not a tty: read from file / pipe. In this mode we don't want any + * limit to the line size, so we call a function to handle that. */ + return linenoiseNoTTY(); + } else if (isUnsupportedTerm()) { + size_t len; - printf("%s",prompt); - fflush(stdout); - if (fgets(buf,LINENOISE_MAX_LINE,stdin) == NULL) return NULL; - len = strlen(buf); - while(len && (buf[len-1] == '\n' || buf[len-1] == '\r')) { - len--; - buf[len] = '\0'; - } - return strdup(buf); - } else { - count = linenoiseRaw(buf,LINENOISE_MAX_LINE,prompt); - if (count == -1) return NULL; - return strdup(buf); - } + printf("%s",prompt); + fflush(stdout); + if (fgets(buf,LINENOISE_MAX_LINE,stdin) == NULL) return NULL; + len = strlen(buf); + while(len && (buf[len-1] == '\n' || buf[len-1] == '\r')) { + len--; + buf[len] = '\0'; + } + return strdup(buf); + } else { + count = linenoiseRaw(buf,LINENOISE_MAX_LINE,prompt); + if (count == -1) return NULL; + return strdup(buf); + } } /* This is just a wrapper the user may want to call in order to make sure @@ -1110,7 +1093,7 @@ char *linenoise(const char *prompt) { * created with. Useful when the main program is using an alternative * allocator. */ void linenoiseFree(void *ptr) { - free(ptr); + free(ptr); } /* ================================ History ================================= */ @@ -1118,19 +1101,19 @@ void linenoiseFree(void *ptr) { /* Free the history, but does not reset it. Only used when we have to * exit() to avoid memory leaks are reported by valgrind & co. */ static void freeHistory(void) { - if (history) { - int j; + if (history) { + int j; - for (j = 0; j < history_len; j++) - free(history[j]); - free(history); - } + for (j = 0; j < history_len; j++) + free(history[j]); + free(history); + } } /* At exit we'll try to fix the terminal to the initial conditions. */ static void linenoiseAtExit(void) { - disableRawMode(STDIN_FILENO); - freeHistory(); + disableRawMode(STDIN_FILENO); + freeHistory(); } /* This is the API call to add a new entry in the linenoise history. @@ -1141,32 +1124,32 @@ static void linenoiseAtExit(void) { * * Using a circular buffer is smarter, but a bit more complex to handle. */ int linenoiseHistoryAdd(const char *line) { - char *linecopy; + char *linecopy; - if (history_max_len == 0) return 0; + if (history_max_len == 0) return 0; - /* Initialization on first call. */ - if (history == NULL) { - history = malloc(sizeof(char*)*history_max_len); - if (history == NULL) return 0; - memset(history,0,(sizeof(char*)*history_max_len)); - } + /* Initialization on first call. */ + if (history == NULL) { + history = malloc(sizeof(char*)*history_max_len); + if (history == NULL) return 0; + memset(history,0,(sizeof(char*)*history_max_len)); + } - /* Don't add duplicated lines. */ - if (history_len && !strcmp(history[history_len-1], line)) return 0; + /* Don't add duplicated lines. */ + if (history_len && !strcmp(history[history_len-1], line)) return 0; - /* Add an heap allocated copy of the line in the history. - * If we reached the max length, remove the older line. */ - linecopy = strdup(line); - if (!linecopy) return 0; - if (history_len == history_max_len) { - free(history[0]); - memmove(history,history+1,sizeof(char*)*(history_max_len-1)); - history_len--; - } - history[history_len] = linecopy; - history_len++; - return 1; + /* Add an heap allocated copy of the line in the history. + * If we reached the max length, remove the older line. */ + linecopy = strdup(line); + if (!linecopy) return 0; + if (history_len == history_max_len) { + free(history[0]); + memmove(history,history+1,sizeof(char*)*(history_max_len-1)); + history_len--; + } + history[history_len] = linecopy; + history_len++; + return 1; } /* Set the maximum length for the history. This function can be called even @@ -1174,48 +1157,48 @@ int linenoiseHistoryAdd(const char *line) { * just the latest 'len' elements if the new history length value is smaller * than the amount of items already inside the history. */ int linenoiseHistorySetMaxLen(int len) { - char **new; + char **new; - if (len < 1) return 0; - if (history) { - int tocopy = history_len; + if (len < 1) return 0; + if (history) { + int tocopy = history_len; - new = malloc(sizeof(char*)*len); - if (new == NULL) return 0; + new = malloc(sizeof(char*)*len); + if (new == NULL) return 0; - /* If we can't copy everything, free the elements we'll not use. */ - if (len < tocopy) { - int j; + /* If we can't copy everything, free the elements we'll not use. */ + if (len < tocopy) { + int j; - for (j = 0; j < tocopy-len; j++) free(history[j]); - tocopy = len; - } - memset(new,0,sizeof(char*)*len); - memcpy(new,history+(history_len-tocopy), sizeof(char*)*tocopy); - free(history); - history = new; - } - history_max_len = len; - if (history_len > history_max_len) - history_len = history_max_len; - return 1; + for (j = 0; j < tocopy-len; j++) free(history[j]); + tocopy = len; + } + memset(new,0,sizeof(char*)*len); + memcpy(new,history+(history_len-tocopy), sizeof(char*)*tocopy); + free(history); + history = new; + } + history_max_len = len; + if (history_len > history_max_len) + history_len = history_max_len; + return 1; } /* Save the history in the specified file. On success 0 is returned * otherwise -1 is returned. */ int linenoiseHistorySave(const char *filename) { - mode_t old_umask = umask(S_IXUSR|S_IRWXG|S_IRWXO); - FILE *fp; - int j; + mode_t old_umask = umask(S_IXUSR|S_IRWXG|S_IRWXO); + FILE *fp; + int j; - fp = fopen(filename,"w"); - umask(old_umask); - if (fp == NULL) return -1; - chmod(filename,S_IRUSR|S_IWUSR); - for (j = 0; j < history_len; j++) - fprintf(fp,"%s\n",history[j]); - fclose(fp); - return 0; + fp = fopen(filename,"w"); + umask(old_umask); + if (fp == NULL) return -1; + chmod(filename,S_IRUSR|S_IWUSR); + for (j = 0; j < history_len; j++) + fprintf(fp,"%s\n",history[j]); + fclose(fp); + return 0; } /* Load the history from the specified file. If the file does not exist @@ -1224,19 +1207,19 @@ int linenoiseHistorySave(const char *filename) { * If the file exists and the operation succeeded 0 is returned, otherwise * on error -1 is returned. */ int linenoiseHistoryLoad(const char *filename) { - FILE *fp = fopen(filename,"r"); - char buf[LINENOISE_MAX_LINE]; + FILE *fp = fopen(filename,"r"); + char buf[LINENOISE_MAX_LINE]; - if (fp == NULL) return -1; + if (fp == NULL) return -1; - while (fgets(buf,LINENOISE_MAX_LINE,fp) != NULL) { - char *p; + while (fgets(buf,LINENOISE_MAX_LINE,fp) != NULL) { + char *p; - p = strchr(buf,'\r'); - if (!p) p = strchr(buf,'\n'); - if (p) *p = '\0'; - linenoiseHistoryAdd(buf); - } - fclose(fp); - return 0; -} + p = strchr(buf,'\r'); + if (!p) p = strchr(buf,'\n'); + if (p) *p = '\0'; + linenoiseHistoryAdd(buf); + } + fclose(fp); + return 0; +} \ No newline at end of file diff --git a/csvreader.cpp b/csvreader.cpp index 22afd62..08d2bdf 100644 --- a/csvreader.cpp +++ b/csvreader.cpp @@ -20,7 +20,7 @@ namespace usql { std::string field; std::vector> parsed_data; - parsed_data.reserve(128); + parsed_data.reserve(256); std::vector line; line.reserve(32); diff --git a/lexer.cpp b/lexer.cpp index fc184a4..97e2126 100644 --- a/lexer.cpp +++ b/lexer.cpp @@ -45,7 +45,7 @@ namespace usql { match_str = stringLiteral(match_str); if (token_type != TokenType::newline) - m_tokens.push_back(Token{match_str, token_type}); + m_tokens.emplace_back(match_str, token_type); } // DEBUG IT @@ -56,8 +56,8 @@ namespace usql { void Lexer::debugTokens() { int i = 0; - for (std::vector::iterator it = m_tokens.begin(); it != m_tokens.end(); ++it) { - std::cerr << i << "\t" << it->token_string << std::endl; + for (auto & m_token : m_tokens) { + std::cerr << i << "\t" << m_token.token_string << std::endl; i++; } } @@ -197,6 +197,10 @@ namespace usql { return TokenType::keyword_float; if (token == "varchar") return TokenType::keyword_varchar; + if (token == "date") + return TokenType::keyword_date; + if (token == "boolean") + return TokenType::keyword_bool; if (token == "distinct") return TokenType::keyword_distinct; if (token == "or") @@ -244,7 +248,7 @@ namespace usql { if (!replace) { return str; } - std::string out = ""; + std::string out; out.reserve(str.size()); @@ -384,6 +388,12 @@ namespace usql { case TokenType::keyword_varchar: txt = "varchar"; break; + case TokenType::keyword_date: + txt = "date"; + break; + case TokenType::keyword_bool: + txt = "boolean"; + break; case TokenType::keyword_distinct: txt = "distinct"; break; diff --git a/lexer.h b/lexer.h index c95299c..33e542b 100644 --- a/lexer.h +++ b/lexer.h @@ -47,6 +47,8 @@ namespace usql { keyword_integer, keyword_float, keyword_varchar, + keyword_date, + keyword_bool, keyword_distinct, int_number, double_number, @@ -103,7 +105,7 @@ namespace usql { private: TokenType type(const std::string &token); - std::string stringLiteral(std::string token); + static std::string stringLiteral(std::string token); static std::string typeToString(TokenType token_type); diff --git a/main.cpp b/main.cpp index 97d812a..3b53f2e 100644 --- a/main.cpp +++ b/main.cpp @@ -126,23 +126,31 @@ void repl() { int main(int argc, char *argv[]) { std::vector sql_commands{ - "create table a (i integer not null, s varchar(64), f float null)", - "insert into a (i, s) values(1, upper('one'))", +// boolean datatype "create table ticker ( tablee varchar(3) not null, permaticker integer, ticker varchar(8) not null, name varchar(256) not null, exchange varchar(32), isdelisted boolean, category varchar(32), cusips varchar(256), siccode integer, sicsector varchar(256), sicindustry varchar(256), famasector varchar(256), famaindustry varchar(256), sector varchar(128), industry varchar(128), scalemarketcap varchar(64), scalerevenue varchar(64), relatedtickers varchar(128), currency varchar(3), location varchar(64), lastupdated date, firstadded date, firstpricedate date, lastpricedate date, firstquarter date, lastquarter date, secfilings varchar(256), companysite varchar(256))" +"create table ticker ( tablee varchar(5) not null, permaticker integer, ticker varchar(10) not null, name varchar(256) not null, exchange varchar(32), isdelisted boolean, category varchar(32), cusips varchar(256), siccode integer, sicsector varchar(256), sicindustry varchar(256), famasector varchar(256), famaindustry varchar(256), sector varchar(128), industry varchar(128), scalemarketcap varchar(64), scalerevenue varchar(64), relatedtickers varchar(128), currency varchar(3), location varchar(64), lastupdated date, firstadded date, firstpricedate date, lastpricedate date, firstquarter date, lastquarter date, secfilings varchar(256), companysite varchar(256))", + "load ticker from '/Users/vaclavt/Library/Mobile Documents/com~apple~CloudDocs/Development/usql/tickers.csv')", + "select permaticker, ticker, name from ticker where ticker = 'WFC'" +// "select * from ticker limit 5" +// "create table a (i integer not null, s varchar(64), f float null, d date null, b boolean)", +// "insert into a (i, s, b) values(1, upper('one'), 'Y')", +// "select i, s, b from a where i >=1 order by 1 desc offset 0 limit 1", // "update table a set s = 'null string aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'", // "update table a set i = null", - "insert into a (i, s) values(2, 'two')", - "insert into a (i, s) values(3, 'two')", - "insert into a (i, s) values(4, lower('FOUR'))", - "insert into a (i, s) values(5, 'five')", - "insert into a (i, s) values(to_date('20.12.1973', '%d.%m.%Y'), 'six')", - "save table a into '/tmp/a.csv'", - "select i, s from a where i > 2 order by 1 desc offset 1 limit 1", - "select distinct s from a", +// "insert into a (i, s) values(2, 'two')", +// "insert into a (i, s) values(3, 'two')", +// "insert into a (i, s) values(4, lower('FOUR'))", +// "insert into a (i, s) values(5, 'five')", +// "insert into a (i, s) values(to_date('20.12.1973', '%d.%m.%Y'), 'six')", +// tohle zpusobi kresh "insert into a (i, d) values(6', '2006-10-04')", +// "insert into a (i, d) values(6, '2006-10-04')", +// "save table a into '/tmp/a.csv'", +// "select i, s from a where i > 2 order by 1 desc offset 1 limit 1", +// "select distinct s, d from a", // "select i, s from a where i = 1", // "select i, s from a where s = 'two'", // "select i, s from a where i <= 3 and s = 'one'", // "select i, s from a where i > 0", - "delete from a where i = 4", +// "delete from a where i = 4", // "select i, s from a where i > 0", // "update a set f = 9.99 where i = 3", // "select i, s, f from a where i = 3", @@ -157,10 +165,10 @@ int main(int argc, char *argv[]) { // "drop table x", // "select i, s, f from a where i > 300", // "select i, to_string(i, '%d.%m.%Y'), s, f from a where i > 300", - "create table prices (datetime integer, symbol varchar(8), prev_close float, open float, price float, change float, change_prct varchar(16))", - "load prices from '/Users/vaclavt/Library/Mobile Documents/com~apple~CloudDocs/Development/usql/prices.csv'", +// "create table prices (datetime integer, symbol varchar(8), prev_close float, open float, price float, change float, change_prct varchar(16))", +// "load prices from '/Users/vaclavt/Library/Mobile Documents/com~apple~CloudDocs/Development/usql/prices.csv'", // "insert into prices (datetime, symbol, prev_close, open, price, change, change_prct) values (1626979443, 'MPC', 54.08, 53.82, 53.63, -0.832101, '-0.83 %')", - "select to_string(datetime, '%d.%m.%Y %H:%M:%S'), symbol, prev_close, open, price, change, change_prct, datetime from prices where symbol = 'SYF' order by 8 desc limit 10" +// "select to_string(datetime, '%d.%m.%Y %H:%M:%S'), symbol, prev_close, open, price, change, change_prct, datetime from prices where symbol = 'SYF' order by 8 desc limit 10" }; diff --git a/parser.cpp b/parser.cpp index 67ce77a..8fcde06 100644 --- a/parser.cpp +++ b/parser.cpp @@ -56,8 +56,8 @@ namespace usql { do { std::string column_name; ColumnType column_type; - int column_len{1}; - bool column_nullable{true}; + int column_len = 1; + bool column_nullable = true; // column name if (m_lexer.tokenType() != TokenType::identifier) { @@ -82,8 +82,14 @@ namespace usql { throw Exception("syntax error, expected int number"); } m_lexer.skipToken(TokenType::close_paren); + } else if (m_lexer.tokenType() == TokenType::keyword_date) { + column_type = ColumnType::date_type; + m_lexer.nextToken(); + } else if (m_lexer.tokenType() == TokenType::keyword_bool) { + column_type = ColumnType::bool_type; + m_lexer.nextToken(); } else { - throw Exception("syntax error, column type expected"); + throw Exception("syntax error, column type expected, found " + m_lexer.currentToken().token_string); } if (m_lexer.tokenType() == TokenType::keyword_not) { @@ -94,7 +100,7 @@ namespace usql { m_lexer.nextToken(); } - cols_def.push_back( ColDefNode(column_name, column_type, column_order++, column_len, column_nullable)); + cols_def.emplace_back(column_name, column_type, column_order++, column_len, column_nullable); m_lexer.skipTokenOptional(TokenType::comma); @@ -155,7 +161,7 @@ namespace usql { m_lexer.skipToken(TokenType::open_paren); do { if (m_lexer.tokenType() != TokenType::identifier) { /* TODO handle error */ } - column_names.push_back(m_lexer.consumeCurrentToken().token_string); + column_names.emplace_back(m_lexer.consumeCurrentToken().token_string); m_lexer.skipTokenOptional(TokenType::comma); } while (m_lexer.tokenType() != TokenType::close_paren); @@ -272,7 +278,7 @@ namespace usql { std::vector> values; do { - cols_names.push_back(m_lexer.consumeCurrentToken().token_string); + cols_names.emplace_back(m_lexer.consumeCurrentToken().token_string); m_lexer.skipToken(TokenType::equal); std::unique_ptr left = Parser::parse_operand_node(); @@ -332,9 +338,9 @@ namespace usql { auto token_type = m_lexer.tokenType(); std::string tokenString = m_lexer.consumeCurrentToken().token_string; switch (token_type) { - case TokenType::int_number: - col_index = std::stoi(tokenString); - break; + case TokenType::int_number: + col_index = std::stoi(tokenString); + break; default: throw Exception("column index allowed in order by clause at this moment"); } @@ -346,7 +352,7 @@ namespace usql { asc = false; } - order_cols.push_back(ColOrderNode{col_index, asc}); + order_cols.emplace_back(col_index, asc); m_lexer.skipTokenOptional(TokenType::comma); diff --git a/parser.h b/parser.h index 7ce6478..5222174 100644 --- a/parser.h +++ b/parser.h @@ -2,8 +2,10 @@ #include "lexer.h" #include "exception.h" -#include +#include "ml_date.h" +#include "settings.h" +#include #include namespace usql { @@ -11,7 +13,9 @@ namespace usql { enum class ColumnType { integer_type, float_type, - varchar_type + varchar_type, + date_type, + bool_type }; enum class NodeType { @@ -20,6 +24,7 @@ namespace usql { int_value, float_value, string_value, + bool_value, database_value, logical_operator, relational_operator, @@ -109,11 +114,13 @@ namespace usql { ValueNode(NodeType type) : Node(type) {} virtual bool isNull() { return false; } - virtual long getIntValue() = 0; + virtual long getIntegerValue() = 0; virtual double getDoubleValue() = 0; virtual std::string getStringValue() = 0; + virtual long getDateValue() = 0; + virtual bool getBooleanValue() = 0; - virtual ~ValueNode() {}; + virtual ~ValueNode() = default; }; struct NullValueNode : ValueNode { @@ -122,9 +129,11 @@ namespace usql { bool isNull() override { return true; } - long getIntValue() override { throw Exception("not supported on null value"); }; + long getIntegerValue() override { throw Exception("not supported on null value"); }; double getDoubleValue() override { throw Exception("not supported on null value"); }; std::string getStringValue() override { throw Exception("not supported on null value"); }; + long getDateValue() override { throw Exception("not supported on null value"); }; + bool getBooleanValue() override { return false; }; }; struct IntValueNode : ValueNode { @@ -132,9 +141,11 @@ namespace usql { IntValueNode(long value) : ValueNode(NodeType::int_value), value(value) {} - long getIntValue() override { return value; }; + long getIntegerValue() override { return value; }; double getDoubleValue() override { return (double) value; }; std::string getStringValue() override { return std::to_string(value); } + long getDateValue() override { return value; }; + bool getBooleanValue() override { return value != 0; }; }; struct DoubleValueNode : ValueNode { @@ -142,9 +153,11 @@ namespace usql { DoubleValueNode(double value) : ValueNode(NodeType::float_value), value(value) {} - long getIntValue() override { return (long) value; }; + long getIntegerValue() override { return (long) value; }; double getDoubleValue() override { return value; }; std::string getStringValue() override { return std::to_string(value); } + long getDateValue() override { return (long) value; }; + bool getBooleanValue() override { return value != 0.0; }; }; struct StringValueNode : ValueNode { @@ -152,11 +165,26 @@ namespace usql { StringValueNode(std::string value) : ValueNode(NodeType::string_value), value(value) {} - long getIntValue() override { return std::stoi(value); }; + long getIntegerValue() override { return std::stoi(value); }; double getDoubleValue() override { return std::stod(value); }; std::string getStringValue() override { return value; }; + long getDateValue() override { return Settings::string_to_date(value); }; + bool getBooleanValue() override { return value == "true"; }; }; + struct BooleanValueNode : ValueNode { + bool value; + + BooleanValueNode(bool value) : ValueNode(NodeType::bool_value), value(value) {} + + long getIntegerValue() override { return (long) value; }; + double getDoubleValue() override { return (double) value; }; + std::string getStringValue() override { return value ? "true" : "false"; } + long getDateValue() override { return (long) value; }; + bool getBooleanValue() override { return value; }; + }; + + struct DatabaseValueNode : Node { std::string col_name; diff --git a/row.cpp b/row.cpp index b796da1..68e6f0a 100644 --- a/row.cpp +++ b/row.cpp @@ -8,13 +8,12 @@ namespace usql { } int ColIntegerValue::compare(ColValue * other) { - return other->isNull() ? 1 : m_integer - other->getIntValue(); // null goes to end + return other->isNull() ? 1 : m_integer - other->getIntValue(); // TODO implicit conversion from long to int } int ColDoubleValue::compare(ColValue * other) { - if (other->isNull()) { // null goes to end - return 1; - } + if (other->isNull()) return 1; // null goes to end + double c = m_double - other->getDoubleValue(); return c < 0 ? -1 : c == 0.0 ? 0 : 1; } @@ -23,6 +22,16 @@ namespace usql { return other->isNull() ? 1 : m_string.compare(other->getStringValue()); // null goes to end } + int ColDateValue::compare(ColValue * other) { + return other->isNull() ? 1 : m_date - other->getIntValue(); // TODO implicit conversion from long to int + } + + int ColBooleanValue::compare(ColValue * other) { + if (other->isNull()) return 1; // null goes to end + + return m_bool==other->getBoolValue() ? 0 : m_bool && !other->getBoolValue() ? -1 : 1; // true first + } + Row::Row(int cols_count) { m_columns.reserve(cols_count); for (int i = 0; i < cols_count; i++) { @@ -32,20 +41,27 @@ namespace usql { Row::Row(const Row &other) { m_columns.reserve(other.m_columns.size()); - // TODO fixme this is nonsense + // TODO fixme, here first set cols null and then immediately replace it for (int i = 0; i < other.m_columns.size(); i++) { m_columns.push_back(std::make_unique()); } + // TODO get rid of dynamic_cast for (int i = 0; i < other.m_columns.size(); i++) { - if (ColIntegerValue *other_v = dynamic_cast(other.m_columns[i].get())) { - setColumnValue(i, other_v->getIntValue()); - } - if (ColDoubleValue *other_v = dynamic_cast(other.m_columns[i].get())) { - setColumnValue(i, other_v->getDoubleValue()); - } - if (ColStringValue *other_v = dynamic_cast(other.m_columns[i].get())) { - setColumnValue(i, other_v->getStringValue()); + if (auto *other_v = dynamic_cast(other.m_columns[i].get())) { + setIntColumnValue(i, other_v->getIntValue()); + } else if (auto *other_v = dynamic_cast(other.m_columns[i].get())) { + setFloatColumnValue(i, other_v->getDoubleValue()); + } else if (auto *other_v = dynamic_cast(other.m_columns[i].get())) { + setStringColumnValue(i, other_v->getStringValue()); + } else if (auto *other_v = dynamic_cast(other.m_columns[i].get())) { + setDateColumnValue(i, other_v->getDateValue()); + } else if (auto *other_v = dynamic_cast(other.m_columns[i].get())) { + setBoolColumnValue(i, other_v->getBoolValue()); + } else if (auto *other_v = dynamic_cast(other.m_columns[i].get())) { + // NOP + } else { + throw Exception("unsupported data type"); } } } @@ -59,43 +75,70 @@ namespace usql { m_columns[col_index] = std::make_unique(); } - void Row::setColumnValue(int col_index, long value) { + void Row::setIntColumnValue(int col_index, long value) { m_columns[col_index] = std::make_unique(value); } - void Row::setColumnValue(int col_index, double value) { + void Row::setFloatColumnValue(int col_index, double value) { m_columns[col_index] = std::make_unique(value); } - void Row::setColumnValue(int col_index, const std::string &value) { + void Row::setStringColumnValue(int col_index, const std::string &value) { m_columns[col_index] = std::make_unique(value); - }; + } + + void Row::setDateColumnValue(int col_index, long value) { + m_columns[col_index] = std::make_unique(value); + } + + void Row::setDateColumnValue(int col_index, const std::string &value) { + m_columns[col_index] = std::make_unique(Settings::string_to_date(value)); + } + + void Row::setBoolColumnValue(int col_index, bool value) { + m_columns[col_index] = std::make_unique(value); + } + + void Row::setBoolColumnValue(int col_index, const std::string &value) { + bool v = (value=="Y" || value=="1"); + m_columns[col_index] = std::make_unique(v); + } void Row::setColumnValue(ColDefNode *col_def, ColValue *col_value) { - if (!col_value->isNull()) { - if (col_def->type == ColumnType::integer_type) - setColumnValue(col_def->order, col_value->getIntValue()); - else if (col_def->type == ColumnType::float_type) - setColumnValue(col_def->order, col_value->getDoubleValue()); - else if (col_def->type == ColumnType::varchar_type) - setColumnValue(col_def->order, col_value->getStringValue()); - } else { - setColumnNull(col_def->order); - } + if (!col_value->isNull()) { + if (col_def->type == ColumnType::integer_type) + setIntColumnValue(col_def->order, col_value->getIntValue()); + else if (col_def->type == ColumnType::float_type) + setFloatColumnValue(col_def->order, col_value->getDoubleValue()); + else if (col_def->type == ColumnType::varchar_type) + setStringColumnValue(col_def->order, col_value->getStringValue()); + else if (col_def->type == ColumnType::date_type) + setDateColumnValue(col_def->order, col_value->getDateValue()); + else if (col_def->type == ColumnType::bool_type) + setBoolColumnValue(col_def->order, col_value->getBoolValue()); + } else { + setColumnNull(col_def->order); + } } - void Row::setColumnValue(ColDefNode *col_def, ValueNode *col_value) { - if (!col_value->isNull()) { - if (col_def->type == ColumnType::integer_type) - setColumnValue(col_def->order, col_value->getIntValue()); - else if (col_def->type == ColumnType::float_type) - setColumnValue(col_def->order, col_value->getDoubleValue()); - else if (col_def->type == ColumnType::varchar_type) - setColumnValue(col_def->order, col_value->getStringValue()); - } else { - setColumnNull(col_def->order); - } - } +void Row::setColumnValue(ColDefNode *col_def, ValueNode *col_value) { + if (!col_value->isNull()) { + if (col_def->type == ColumnType::integer_type) + setIntColumnValue(col_def->order, col_value->getIntegerValue()); + else if (col_def->type == ColumnType::float_type) + setFloatColumnValue(col_def->order, col_value->getDoubleValue()); + else if (col_def->type == ColumnType::varchar_type) + setStringColumnValue(col_def->order, col_value->getStringValue()); + else if (col_def->type == ColumnType::date_type) + setIntColumnValue(col_def->order, col_value->getDateValue()); + else if (col_def->type == ColumnType::bool_type) + setBoolColumnValue(col_def->order, col_value->getBooleanValue()); + else + throw Exception("unsupported data type"); + } else { + setColumnNull(col_def->order); + } +} int Row::compare(const Row & other) const { for (int ci = 0; ci < m_columns.size(); ci++) { diff --git a/row.h b/row.h index 7df1c31..a50b99a 100644 --- a/row.h +++ b/row.h @@ -2,44 +2,48 @@ #include "exception.h" #include "parser.h" +#include "settings.h" #include namespace usql { - struct ColValue { - virtual bool isNull() { return false; }; virtual long getIntValue() = 0; virtual double getDoubleValue() = 0; virtual std::string getStringValue() = 0; + virtual long getDateValue() = 0; + virtual bool getBoolValue() = 0; virtual int compare(ColValue * other) = 0; virtual ~ColValue() = default; + }; struct ColNullValue : ColValue { - bool isNull() override { return true; }; long getIntValue() override { throw Exception("Not supported"); }; double getDoubleValue() override { throw Exception("Not supported"); }; std::string getStringValue() override { return "null"; }; + long getDateValue() override { throw Exception("Not supported"); }; + bool getBoolValue() override { throw Exception("Not supported"); }; int compare(ColValue * other) override; }; struct ColIntegerValue : ColValue { - ColIntegerValue(long value) : m_integer(value) {}; ColIntegerValue(const ColIntegerValue &other) : m_integer(other.m_integer) {}; long getIntValue() override { return m_integer; }; double getDoubleValue() override { return (double) m_integer; }; std::string getStringValue() override { return std::to_string(m_integer); }; + long getDateValue() override { return m_integer; }; + bool getBoolValue() override { throw Exception("Not supported"); }; int compare(ColValue * other) override; @@ -48,13 +52,14 @@ namespace usql { struct ColDoubleValue : ColValue { - ColDoubleValue(double value) : m_double(value) {}; ColDoubleValue(const ColDoubleValue &other) : m_double(other.m_double) {} long getIntValue() override { return (long) m_double; }; double getDoubleValue() override { return m_double; }; std::string getStringValue() override { return std::to_string(m_double); }; + long getDateValue() override { return (long) m_double; }; + bool getBoolValue() override { throw Exception("Not supported"); }; int compare(ColValue * other) override; @@ -63,19 +68,49 @@ namespace usql { struct ColStringValue : ColValue { - ColStringValue(const std::string &value) : m_string(value) {}; ColStringValue(const ColStringValue &other) : m_string(other.m_string) {}; long getIntValue() override { return std::stoi(m_string); }; double getDoubleValue() override { return std::stod(m_string); }; std::string getStringValue() override { return m_string; }; + long getDateValue() override { return std::stoi(m_string); }; + bool getBoolValue() override { throw Exception("Not supported"); }; int compare(ColValue * other) override; std::string m_string; }; + struct ColDateValue : ColValue { + ColDateValue(long value) : m_date(value) {}; + ColDateValue(const ColDateValue &other) : m_date(other.m_date) {}; + + long getIntValue() override { return m_date; }; + double getDoubleValue() override { return (double) m_date; }; + std::string getStringValue() override { return Settings::date_to_string(m_date); }; + long getDateValue() override { return m_date; }; + bool getBoolValue() override { throw Exception("Not supported"); }; + + int compare(ColValue * other) override; + + long m_date; // seconds since epoch for now + }; + + struct ColBooleanValue : ColValue { + ColBooleanValue(bool value) : m_bool(value) {}; + ColBooleanValue(const ColBooleanValue &other) : m_bool(other.m_bool) {}; + + long getIntValue() override { return (long) m_bool; }; + double getDoubleValue() override { return (double) m_bool; }; + std::string getStringValue() override { return m_bool ? "Y" : "N"; }; + long getDateValue() override { throw Exception("Not supported"); }; + bool getBoolValue() override { return m_bool; }; + + int compare(ColValue * other) override; + + bool m_bool; + }; class Row { @@ -87,9 +122,13 @@ namespace usql { bool operator==(const Row &other) const {return this->compare(other) == 0; }; void setColumnNull(int col_index); - void setColumnValue(int col_index, long value); - void setColumnValue(int col_index, double value); - void setColumnValue(int col_index, const std::string &value); + void setIntColumnValue(int col_index, long value); + void setFloatColumnValue(int col_index, double value); + void setStringColumnValue(int col_index, const std::string &value); + void setDateColumnValue(int col_index, long value); + void setDateColumnValue(int col_index, const std::string &value); + void setBoolColumnValue(int col_index, bool value); + void setBoolColumnValue(int col_index, const std::string &value); void setColumnValue(ColDefNode *col_def, ColValue *col_value); void setColumnValue(ColDefNode *col_def, ValueNode *col_value); diff --git a/settings.cpp b/settings.cpp new file mode 100644 index 0000000..95cb205 --- /dev/null +++ b/settings.cpp @@ -0,0 +1,30 @@ + +#include "settings.h" + +#include "ml_date.h" + +namespace usql { + +std::vector> Settings::m_settings = + { std::make_pair("DATE_FORMAT", "%Y-%m-%d"), + std::make_pair("BOOL_TRUE_LITERAL", "Y"), + std::make_pair("BOOL_FALSE_LITERAL", "N") }; + + +long Settings::string_to_date(const std::string &datestr) { + return ::string_to_date(datestr, get_setting("DATE_FORMAT")); +} + + +std::string Settings::date_to_string(long date) { + return ::date_to_string(date, get_setting("DATE_FORMAT")); +} + +std::basic_string Settings::get_setting(const std::string &name) { + for(const auto& pair : m_settings) { + if (pair.first == name) return pair.second; + } + // TODO exception +} + +} // namespace \ No newline at end of file diff --git a/settings.h b/settings.h new file mode 100644 index 0000000..e2c809e --- /dev/null +++ b/settings.h @@ -0,0 +1,21 @@ +#pragma once + +#include +#include + +namespace usql { + +class Settings { + +public: + static long string_to_date(const std::string &datestr); + static std::string date_to_string(long date); + +private: + static std::basic_string get_setting(const std::string &name); + +private: + static std::vector> m_settings; +}; + +} // namespace \ No newline at end of file diff --git a/table.cpp b/table.cpp index 7466090..974d485 100644 --- a/table.cpp +++ b/table.cpp @@ -6,10 +6,10 @@ namespace usql { -Table::Table(const std::string name, const std::vector columns) { +Table::Table(const std::string& name, const std::vector& columns) { m_name = name; m_col_defs = columns; - m_rows.reserve(16); + m_rows.reserve(256); } Table::Table(const Table &other) { @@ -22,7 +22,7 @@ Table::Table(const Table &other) { } ColDefNode Table::get_column_def(const std::string &col_name) { - auto name_cmp = [col_name](ColDefNode cd) { return cd.name == col_name; }; + auto name_cmp = [col_name](const ColDefNode& cd) { return cd.name == col_name; }; auto col_def = std::find_if(begin(m_col_defs), end(m_col_defs), name_cmp); if (col_def != std::end(m_col_defs)) { return *col_def; @@ -39,8 +39,8 @@ ColDefNode Table::get_column_def(int col_index) { } } -Row Table::create_empty_row() { - return Row(columns_count()); +Row Table::create_empty_row() const { + return {columns_count()}; } std::string Table::csv_string() { @@ -52,12 +52,12 @@ std::string Table::csv_string() { } // rows - for (auto it = m_rows.begin(); it != m_rows.end(); ++it) { + for (auto & m_row : m_rows) { std::string csv_line{"\n"}; for(int i = 0; i < m_col_defs.size(); i++) { if (i > 0) csv_line += ","; - auto col = it->ith_column(i); + auto col = m_row.ith_column(i); if (!col->isNull()) { csv_line += col->getStringValue(); // TODO handle enclosing commas etc } @@ -86,14 +86,20 @@ int Table::load_csv_string(const std::string &content) { for (size_t i = 0; i < columns_count(); i++) { ColDefNode col_def = get_column_def(colDefs[i].name); - // TODO validate value - if (col_def.type == ColumnType::integer_type) { - new_row.setColumnValue(col_def.order, std::stol(csv_line[i])); + if (csv_line[i].empty()) { + new_row.setColumnNull(col_def.order); + } else if (col_def.type == ColumnType::integer_type) { + new_row.setIntColumnValue(col_def.order, string_to_long(csv_line[i])); } else if (col_def.type == ColumnType::float_type) { - new_row.setColumnValue(col_def.order, std::stof(csv_line[i])); - } else { - new_row.setColumnValue(col_def.order, csv_line[i]); - } + new_row.setFloatColumnValue(col_def.order, string_to_double(csv_line[i])); + } else if (col_def.type == ColumnType::varchar_type) { + new_row.setStringColumnValue(col_def.order, csv_line[i]); + } else if (col_def.type == ColumnType::date_type) { + new_row.setDateColumnValue(col_def.order, csv_line[i]); + } else if (col_def.type == ColumnType::bool_type) { + new_row.setBoolColumnValue(col_def.order, csv_line[i]); + } else + throw Exception("unsupported column type"); } // append new_row @@ -105,12 +111,28 @@ int Table::load_csv_string(const std::string &content) { return row_cnt; } +double Table::string_to_double(const std::string &s) { + try { + return std::stod(s); + } catch (std::invalid_argument &e) { + throw Exception("error parsing as double: " + s); + } +} + +long Table::string_to_long(const std::string &s) { + try { + return std::stol(s); + } catch (std::invalid_argument &e) { + throw Exception("error parsing as integer: " + s); + } +} + void Table::print() { std::string out{"| "}; std::string out2{"+-"}; std::vector col_char_sizes{}; - for(auto col_def : m_col_defs) { + for(const auto& col_def : m_col_defs) { int col_size = col_def.type == ColumnType::varchar_type ? col_def.length : 10; col_char_sizes.push_back(col_size); @@ -129,6 +151,10 @@ void Table::print() { } void Table::add_row(const Row &row) { + // PERF, here it is performance botleneck, because + // m_rows.push_back(row) calls Row::Row(const Row &other) constructor + // it would be much more performant to add row directly to m_rows in create_new_row + // and here in case of failed validation only remove it validate_row(row); m_rows.push_back(row); } @@ -143,12 +169,17 @@ void Table::add_copy_of_row(const Row &row) { new_row.setColumnNull(i); } else { if (m_col_defs[i].type == ColumnType::integer_type) { - new_row.setColumnValue(i, row.ith_column(i)->getIntValue()); + new_row.setIntColumnValue(i, row.ith_column(i)->getIntValue()); } else if (m_col_defs[i].type == ColumnType::float_type) { - new_row.setColumnValue(i, row.ith_column(i)->getDoubleValue()); + new_row.setFloatColumnValue(i, row.ith_column(i)->getDoubleValue()); } else if (m_col_defs[i].type == ColumnType::varchar_type) { - new_row.setColumnValue(i, row.ith_column(i)->getStringValue()); - } + new_row.setStringColumnValue(i, row.ith_column(i)->getStringValue()); + } else if (m_col_defs[i].type == ColumnType::date_type) { + new_row.setDateColumnValue(i, row.ith_column(i)->getDateValue()); + } else if (m_col_defs[i].type == ColumnType::bool_type) { + new_row.setBoolColumnValue(i, row.ith_column(i)->getBoolValue()); + } else + throw Exception("unsupported column type"); } } @@ -157,7 +188,7 @@ void Table::add_copy_of_row(const Row &row) { } void Table::validate_column(const ColDefNode *col_def, ValueNode *col_val) { - if (col_def->null == false && col_val->isNull()) { + if (!col_def->null && col_val->isNull()) { throw Exception("Column " + col_def->name + " cannot be null"); } if (col_def->type == ColumnType::varchar_type && !col_val->isNull() && col_val->getStringValue().size() > col_def->length) { @@ -166,7 +197,7 @@ void Table::validate_column(const ColDefNode *col_def, ValueNode *col_val) { } void Table::validate_column(const ColDefNode *col_def, ColValue *col_val) { - if (col_def->null == false && col_val->isNull()) { + if (!col_def->null && col_val->isNull()) { throw Exception("Column " + col_def->name + " cannot be null"); } if (col_def->type == ColumnType::varchar_type && !col_val->isNull() && col_val->getStringValue().size() > col_def->length) { diff --git a/table.h b/table.h index f7ef380..31ee39e 100644 --- a/table.h +++ b/table.h @@ -10,7 +10,7 @@ namespace usql { struct Table { Table(const Table &other); - Table(const std::string name, const std::vector columns); + Table(const std::string& name, const std::vector& columns); ColDefNode get_column_def(const std::string &col_name); ColDefNode get_column_def(int col_index); @@ -18,12 +18,12 @@ namespace usql { int columns_count() const { return m_col_defs.size(); }; int rows_count() const { return m_rows.size(); }; - Row create_empty_row(); // TODO this means unnecessary copying + Row create_empty_row() const; // TODO this means unnecessary copying void add_row(const Row &row); void add_copy_of_row(const Row &row); - void validate_column(const ColDefNode *col_def, ValueNode *col_val); - void validate_column(const ColDefNode *col_def, ColValue *col_val); + static void validate_column(const ColDefNode *col_def, ValueNode *col_val); + static void validate_column(const ColDefNode *col_def, ColValue *col_val); void validate_row(const Row &row); std::string csv_string(); @@ -34,6 +34,9 @@ namespace usql { std::string m_name; std::vector m_col_defs; std::vector m_rows; + + static long string_to_long(const std::string &s) ; + static double string_to_double(const std::string &s) ; }; } \ No newline at end of file diff --git a/usql.cpp b/usql.cpp index d947be3..2757495 100644 --- a/usql.cpp +++ b/usql.cpp @@ -199,7 +199,7 @@ std::unique_ptr USql::execute_select(SelectFromTableNode &node) { return std::move(result); } -void USql::execute_distinct(SelectFromTableNode &node, Table *result) const { +void USql::execute_distinct(SelectFromTableNode &node, Table *result) { if (!node.distinct) return; auto compare_rows = [](const Row &a, const Row &b) { return a.compare(b) >= 0; }; @@ -208,19 +208,19 @@ void USql::execute_distinct(SelectFromTableNode &node, Table *result) const { result->m_rows.erase(std::unique(result->m_rows.begin(), result->m_rows.end()), result->m_rows.end()); } -void USql::execute_order_by(SelectFromTableNode &node, Table *table, Table *result) const { - if (node.order_by.size() == 0) return; +void USql::execute_order_by(SelectFromTableNode &node, Table *table, Table *result) { + if (node.order_by.empty()) return; - auto compare_rows = [&node, &result, this](const Row &a, const Row &b) { - for(auto order_by_col_def : node.order_by) { + auto compare_rows = [&node, &result](const Row &a, const Row &b) { + for(const auto& order_by_col_def : node.order_by) { ColDefNode col_def = result->get_column_def(order_by_col_def.col_index - 1); // TODO validate index ColValue *a_val = a.ith_column(col_def.order); ColValue *b_val = b.ith_column(col_def.order); int compare = a_val->compare(b_val); - if (compare < 0) return order_by_col_def.ascending ? true : false; - if (compare > 0) return order_by_col_def.ascending ? false : true; + if (compare < 0) return order_by_col_def.ascending; + if (compare > 0) return !order_by_col_def.ascending; } return false; }; @@ -228,7 +228,7 @@ void USql::execute_order_by(SelectFromTableNode &node, Table *table, Table *resu std::sort(result->m_rows.begin(), result->m_rows.end(), compare_rows); } -void USql::execute_offset_limit(OffsetLimitNode &node, Table *result) const { +void USql::execute_offset_limit(OffsetLimitNode &node, Table *result) { if (node.offset > 0) result->m_rows.erase(result->m_rows.begin(), result->rows_count() > node.offset ? result->m_rows.begin() + node.offset : result->m_rows.end()); @@ -270,7 +270,7 @@ std::unique_ptr
USql::execute_delete(DeleteFromTableNode &node) { table->m_rows.erase( std::remove_if(table->m_rows.begin(), table->m_rows.end(), - [&node, table, this](Row &row){return eval_where(node.where.get(), table, row);}), + [&node, table](Row &row){return eval_where(node.where.get(), table, row);}), table->m_rows.end()); affected_rows -= table->rows_count(); @@ -295,7 +295,7 @@ std::unique_ptr
USql::execute_update(UpdateTableNode &node) { static_cast(*node.values[i]), table, *row); - table->validate_column(&col_def, new_val.get()); + usql::Table::validate_column(&col_def, new_val.get()); row->setColumnValue(&col_def, new_val.get()); i++; } @@ -308,7 +308,7 @@ std::unique_ptr
USql::execute_update(UpdateTableNode &node) { } -bool USql::eval_where(Node *where, Table *table, Row &row) const { +bool USql::eval_where(Node *where, Table *table, Row &row) { switch (where->node_type) { // no where clause case NodeType::true_node: return true; @@ -324,20 +324,25 @@ bool USql::eval_where(Node *where, Table *table, Row &row) const { } -bool USql::eval_relational_operator(const RelationalOperatorNode &filter, Table *table, Row &row) const { +bool USql::eval_relational_operator(const RelationalOperatorNode &filter, Table *table, Row &row) { std::unique_ptr left_value = eval_value_node(table, row, filter.left.get()); std::unique_ptr right_value = eval_value_node(table, row, filter.right.get()); double comparator; if (left_value->node_type == NodeType::int_value && right_value->node_type == NodeType::int_value) { - comparator = left_value->getIntValue() - right_value->getIntValue(); + comparator = left_value->getIntegerValue() - right_value->getIntegerValue(); } else if ((left_value->node_type == NodeType::int_value && right_value->node_type == NodeType::float_value) || (left_value->node_type == NodeType::float_value && right_value->node_type == NodeType::int_value) || (left_value->node_type == NodeType::float_value && right_value->node_type == NodeType::float_value)) { comparator = left_value->getDoubleValue() - right_value->getDoubleValue(); } else if (left_value->node_type == NodeType::string_value || right_value->node_type == NodeType::string_value) { comparator = left_value->getStringValue().compare(right_value->getStringValue()); + } else if (left_value->node_type == NodeType::bool_value && right_value->node_type == NodeType::bool_value) { + bool bl = left_value->getBooleanValue(); + bool br = right_value->getBooleanValue(); + comparator = bl == br ? 0 : 1; // TODO define it + // TODO handle dates } else { // TODO throw exception } @@ -364,10 +369,8 @@ bool USql::eval_relational_operator(const RelationalOperatorNode &filter, Table std::unique_ptr USql::eval_value_node(Table *table, Row &row, Node *node) { if (node->node_type == NodeType::database_value || node->node_type == NodeType::column_name) { // TODO sjednotit return eval_database_value_node(table, row, node); - - } else if (node->node_type == NodeType::int_value || node->node_type == NodeType::float_value || node->node_type == NodeType::string_value) { + } else if (node->node_type == NodeType::int_value || node->node_type == NodeType::float_value || node->node_type == NodeType::string_value || node->node_type == NodeType::bool_value) { return eval_literal_value_node(table, row, node); - } else if (node->node_type == NodeType::function) { return eval_function_value_node(table, row, node); } else if (node->node_type == NodeType::null_value) { @@ -382,15 +385,18 @@ std::unique_ptr USql::eval_database_value_node(Table *table, Row &row ColDefNode col_def = table->get_column_def( dvl->col_name); // TODO optimize it to just get this def once auto db_value = row.ith_column(col_def.order); - if (col_def.type == ColumnType::integer_type) { + if (col_def.type == ColumnType::integer_type) return std::make_unique(db_value->getIntValue()); - } - if (col_def.type == ColumnType::float_type) { + if (col_def.type == ColumnType::float_type) return std::make_unique(db_value->getDoubleValue()); - } - if (col_def.type == ColumnType::varchar_type) { + if (col_def.type == ColumnType::varchar_type) return std::make_unique(db_value->getStringValue()); - } + if (col_def.type == ColumnType::bool_type) + return std::make_unique(db_value->getBoolValue()); + // Date has no it's own value node (it is passed around as string) + if (col_def.type == ColumnType::date_type) + return std::make_unique(db_value->getStringValue()); + throw Exception("unknown database value type"); } @@ -408,7 +414,11 @@ std::unique_ptr USql::eval_literal_value_node(Table *table, Row &row, auto *ivl = static_cast(node); return std::make_unique(ivl->value); + } else if (node->node_type == NodeType::bool_value) { + auto *ivl = static_cast(node); + return std::make_unique(ivl->value); } + // Date has no it's own value node (it is passed around as string) throw Exception("invalid type"); } @@ -438,10 +448,10 @@ std::unique_ptr USql::eval_function_value_node(Table *table, Row &row std::string date = evaluatedPars[0]->getStringValue(); std::string format = evaluatedPars[1]->getStringValue(); long epoch_time = string_to_date(date, format); - return std::make_unique(epoch_time); + return std::make_unique(epoch_time); // No DateValueNode for now } if (fnc->function == "to_string") { - long date = evaluatedPars[0]->getIntValue(); + long date = evaluatedPars[0]->getDateValue(); std::string format = evaluatedPars[1]->getStringValue(); std::string formated_date = date_to_string(date, format); return std::make_unique(formated_date); @@ -451,7 +461,7 @@ std::unique_ptr USql::eval_function_value_node(Table *table, Row &row } -bool USql::eval_logical_operator(LogicalOperatorNode &node, Table *pTable, Row &row) const { +bool USql::eval_logical_operator(LogicalOperatorNode &node, Table *pTable, Row &row) { bool left = eval_relational_operator(static_cast(*node.left), pTable, row); if ((node.op == LogicalOperatorType::and_operator && !left) || (node.op == LogicalOperatorType::or_operator && left)) @@ -462,7 +472,7 @@ bool USql::eval_logical_operator(LogicalOperatorNode &node, Table *pTable, Row & } -std::unique_ptr USql::eval_arithmetic_operator(ColumnType outType, ArithmeticalOperatorNode &node, Table *table, Row &row) const { +std::unique_ptr USql::eval_arithmetic_operator(ColumnType outType, ArithmeticalOperatorNode &node, Table *table, Row &row) { if (node.op == ArithmeticalOperatorType::copy_value) { return eval_value_node(table, row, node.left.get()); } @@ -487,8 +497,8 @@ std::unique_ptr USql::eval_arithmetic_operator(ColumnType outType, Ar } } else if (outType == ColumnType::integer_type) { - long l = ((ValueNode *) left.get())->getIntValue(); - long r = ((ValueNode *) right.get())->getIntValue(); + long l = ((ValueNode *) left.get())->getIntegerValue(); + long r = ((ValueNode *) right.get())->getIntegerValue(); switch (node.op) { case ArithmeticalOperatorType::plus_operator: return std::make_unique(l + r); @@ -512,6 +522,7 @@ std::unique_ptr USql::eval_arithmetic_operator(ColumnType outType, Ar throw Exception("implement me!!"); } } + // TODO date node should support addition and substraction throw Exception("implement me!!"); } @@ -519,16 +530,16 @@ std::unique_ptr USql::eval_arithmetic_operator(ColumnType outType, Ar std::unique_ptr
USql::create_stmt_result_table(long code, const std::string &text, long affected_rows) { std::vector result_tbl_col_defs{}; - result_tbl_col_defs.push_back(ColDefNode("code", ColumnType::integer_type, 0, 1, false)); - result_tbl_col_defs.push_back(ColDefNode("desc", ColumnType::varchar_type, 1, 48, false)); - result_tbl_col_defs.push_back(ColDefNode("affected_rows", ColumnType::integer_type, 0, 1, true)); + result_tbl_col_defs.emplace_back("code", ColumnType::integer_type, 0, 1, false); + result_tbl_col_defs.emplace_back("desc", ColumnType::varchar_type, 1, 48, false); + result_tbl_col_defs.emplace_back("affected_rows", ColumnType::integer_type, 0, 1, true); auto table_def = std::make_unique
("result", result_tbl_col_defs); Row new_row = table_def->create_empty_row(); - new_row.setColumnValue(0, code); - new_row.setColumnValue(1, text); - new_row.setColumnValue(2, affected_rows); + new_row.setIntColumnValue(0, code); + new_row.setStringColumnValue(1, text); + new_row.setIntColumnValue(2, affected_rows); table_def->add_row(new_row); return std::move(table_def); @@ -554,4 +565,5 @@ void USql::check_table_not_exists(const std::string &name) { } } + } // namespace \ No newline at end of file diff --git a/usql.h b/usql.h index cf9a023..4530c58 100644 --- a/usql.h +++ b/usql.h @@ -13,7 +13,6 @@ class USql { public: USql() = default; - std::unique_ptr
execute(const std::string &command); private: @@ -32,7 +31,7 @@ private: private: - bool eval_where(Node *where, Table *table, Row &row) const; + static bool eval_where(Node *where, Table *table, Row &row) ; static std::unique_ptr eval_value_node(Table *table, Row &row, Node *node); static std::unique_ptr eval_database_value_node(Table *table, Row &row, Node *node); @@ -40,9 +39,9 @@ private: static std::unique_ptr eval_function_value_node(Table *table, Row &row, Node *node); - bool eval_relational_operator(const RelationalOperatorNode &filter, Table *table, Row &row) const; - bool eval_logical_operator(LogicalOperatorNode &node, Table *pTable, Row &row) const; - std::unique_ptr eval_arithmetic_operator(ColumnType outType, ArithmeticalOperatorNode &node, Table *table, Row &row) const; + static bool eval_relational_operator(const RelationalOperatorNode &filter, Table *table, Row &row) ; + static bool eval_logical_operator(LogicalOperatorNode &node, Table *pTable, Row &row) ; + static std::unique_ptr eval_arithmetic_operator(ColumnType outType, ArithmeticalOperatorNode &node, Table *table, Row &row) ; static std::unique_ptr
create_stmt_result_table(long code, const std::string &text, long affected_rows); @@ -55,9 +54,9 @@ private: Parser m_parser; std::list
m_tables; - void execute_distinct(SelectFromTableNode &node, Table *result) const; - void execute_order_by(SelectFromTableNode &node, Table *table, Table *result) const; - void execute_offset_limit(OffsetLimitNode &node, Table *result) const; + static void execute_distinct(SelectFromTableNode &node, Table *result) ; + static void execute_order_by(SelectFromTableNode &node, Table *table, Table *result) ; + static void execute_offset_limit(OffsetLimitNode &node, Table *result) ; }; } // namespace \ No newline at end of file