static inline int scanner_read_more(struct scanner *lc, int n) { bstream *b = lc->rs; int more = 0; while (b->len < b->pos + lc->yycur + n) { if (lc->mode == LINE_1 || !lc->started) return EOF; /* query is not finished ask for more */ if (b->eof || !isa_block_stream(b->s)) { if (mnstr_write(lc->ws, PROMPT2, sizeof(PROMPT2) - 1, 1) == 1) mnstr_flush(lc->ws); b->eof = 0; more = 1; } /* we need more query text */ if (bstream_next(b) < 0 || /* we asked for more data but didn't get any */ (more && b->eof && b->len < b->pos + lc->yycur + n)) return EOF; } return 1; }
str mnstr_flush_streamwrap(int *ret, Stream *S) { stream *s = *(stream **)S; (void)ret; if (mnstr_flush(s)) throw(IO, "streams.flush", "failed to flush stream"); return MAL_SUCCEED; }
/* * The default method to interact with the database server is to connect * using a port number. The first line received should contain * authorization information, such as user name. * * The scheduleClient receives a challenge response consisting of * endian:user:password:lang:database: */ static void exit_streams( bstream *fin, stream *fout ) { if (fout && fout != GDKstdout) { mnstr_flush(fout); mnstr_close(fout); mnstr_destroy(fout); } if (fin) (void) bstream_destroy(fin); }
/* Sends command for database to merovingian listening at host and port. * If host is a path, and port is -1, a UNIX socket connection for host * is opened. The response of merovingian is returned as a malloced * string. If wait is set to a non-zero value, this function will only * return after it has seen an EOF from the server. This is useful with * multi-line responses, but can lock up for single line responses where * the server allows pipelining (and hence doesn't close the * connection). */ char* control_send( char** ret, char* host, int port, char* database, char* command, char wait, char* pass) { char sbuf[8096]; char rbuf[8096]; char *buf; int sock = -1; ssize_t len; stream *fdin = NULL; stream *fdout = NULL; *ret = NULL; /* gets overwritten in case of success */ if (port == -1) { struct sockaddr_un server; /* UNIX socket connect */ if ((sock = socket(PF_UNIX, SOCK_STREAM, 0)) == -1) { snprintf(sbuf, sizeof(sbuf), "cannot open connection: %s", strerror(errno)); return(strdup(sbuf)); } memset(&server, 0, sizeof(struct sockaddr_un)); server.sun_family = AF_UNIX; strncpy(server.sun_path, host, sizeof(server.sun_path) - 1); if (connect(sock, (SOCKPTR) &server, sizeof(struct sockaddr_un)) == -1) { snprintf(sbuf, sizeof(sbuf), "cannot connect: %s", strerror(errno)); close(sock); return(strdup(sbuf)); } } else { struct sockaddr_in server; struct hostent *hp; char ver = 0; char *p; /* TCP socket connect */ if ((sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) { snprintf(sbuf, sizeof(sbuf), "cannot open connection: %s", strerror(errno)); return(strdup(sbuf)); } hp = gethostbyname(host); if (hp == NULL) { snprintf(sbuf, sizeof(sbuf), "cannot lookup hostname: %s", hstrerror(h_errno)); close(sock); return(strdup(sbuf)); } memset(&server, 0, sizeof(struct sockaddr_in)); server.sin_family = hp->h_addrtype; memcpy(&server.sin_addr, hp->h_addr_list[0], hp->h_length); server.sin_port = htons((unsigned short) (port & 0xFFFF)); if (connect(sock, (SOCKPTR) &server, sizeof(struct sockaddr_in)) == -1) { snprintf(sbuf, sizeof(sbuf), "cannot connect: %s", strerror(errno)); close(sock); return(strdup(sbuf)); } /* try reading length */ len = recv(sock, rbuf, 2, 0); if (len == 2) len += recv(sock, rbuf + len, sizeof(rbuf) - len - 1, 0); /* perform login ritual */ if (len <= 2) { snprintf(sbuf, sizeof(sbuf), "no response from monetdbd"); close(sock); return(strdup(sbuf)); } rbuf[len] = 0; /* we only understand merovingian:1 and :2 (backwards compat * <=Aug2011) and mapi v9 on merovingian */ if (strncmp(rbuf, "merovingian:1:", strlen("merovingian:1:")) == 0) { buf = rbuf + strlen("merovingian:1:"); ver = 1; } else if (strncmp(rbuf, "merovingian:2:", strlen("merovingian:2:")) == 0) { buf = rbuf + strlen("merovingian:2:"); ver = 2; } else if (strstr(rbuf + 2, ":merovingian:9:") != NULL) { buf = rbuf + 2; ver = 9; fdin = block_stream(socket_rastream(sock, "client in")); fdout = block_stream(socket_wastream(sock, "client out")); } else { if (len > 2 && (strstr(rbuf + 2, ":BIG:") != NULL || strstr(rbuf + 2, ":LIT:") != NULL)) { snprintf(sbuf, sizeof(sbuf), "cannot connect: " "server looks like a mapi server, " "are you connecting to an mserver directly " "instead of monetdbd?"); } else { snprintf(sbuf, sizeof(sbuf), "cannot connect: " "unsupported monetdbd server"); } close(sock); return(strdup(sbuf)); } switch (ver) { case 1: case 2: /* we never really used the mode specifier of v2 */ p = strchr(buf, ':'); if (p != NULL) *p = '\0'; p = control_hash(pass, buf); len = snprintf(sbuf, sizeof(sbuf), "%s%s\n", p, ver == 2 ? ":control" : ""); len = send(sock, sbuf, len, 0); free(p); if (len == -1) { close(sock); return(strdup("cannot send challenge response to server")); } break; case 9: { char *chal = NULL; char *algos = NULL; char *shash = NULL; char *phash = NULL; char *algsv[] = { "RIPEMD160", "SHA256", "SHA1", "MD5", NULL }; char **algs = algsv; /* buf at this point looks like * "challenge:servertype:protover:algos:endian:hash:" */ chal = buf; /* chal */ p = strchr(chal, ':'); if (p == NULL) { snprintf(sbuf, sizeof(sbuf), "cannot connect: " "invalid challenge from monetdbd server"); close_stream(fdout); close_stream(fdin); return(strdup(sbuf)); } *p++ = '\0'; /* servertype */ p = strchr(p, ':'); if (p == NULL) { snprintf(sbuf, sizeof(sbuf), "cannot connect: " "invalid challenge from monetdbd server"); close_stream(fdout); close_stream(fdin); return(strdup(sbuf)); } *p++ = '\0'; /* protover */ p = strchr(p, ':'); if (p == NULL) { snprintf(sbuf, sizeof(sbuf), "cannot connect: " "invalid challenge from monetdbd server"); close_stream(fdout); close_stream(fdin); return(strdup(sbuf)); } *p++ = '\0'; /* algos */ algos = p; p = strchr(p, ':'); if (p == NULL) { snprintf(sbuf, sizeof(sbuf), "cannot connect: " "invalid challenge from monetdbd server"); close_stream(fdout); close_stream(fdin); return(strdup(sbuf)); } *p++ = '\0'; /* endian */ p = strchr(p, ':'); if (p == NULL) { snprintf(sbuf, sizeof(sbuf), "cannot connect: " "invalid challenge from monetdbd server"); close_stream(fdout); close_stream(fdin); return(strdup(sbuf)); } *p++ = '\0'; /* hash */ shash = p; p = strchr(p, ':'); if (p == NULL) { snprintf(sbuf, sizeof(sbuf), "cannot connect: " "invalid challenge from monetdbd server"); close_stream(fdout); close_stream(fdin); return(strdup(sbuf)); } *p = '\0'; /* we first need to hash our password in the form the * server stores it too */ if (strcmp(shash, "RIPEMD160") == 0) { phash = mcrypt_RIPEMD160Sum(pass, strlen(pass)); } else if (strcmp(shash, "SHA512") == 0) { phash = mcrypt_SHA512Sum(pass, strlen(pass)); } else if (strcmp(shash, "SHA384") == 0) { phash = mcrypt_SHA384Sum(pass, strlen(pass)); } else if (strcmp(shash, "SHA256") == 0) { phash = mcrypt_SHA256Sum(pass, strlen(pass)); } else if (strcmp(shash, "SHA224") == 0) { phash = mcrypt_SHA224Sum(pass, strlen(pass)); } else if (strcmp(shash, "SHA1") == 0) { phash = mcrypt_SHA1Sum(pass, strlen(pass)); } else if (strcmp(shash, "MD5") == 0) { phash = mcrypt_MD5Sum(pass, strlen(pass)); } else { snprintf(sbuf, sizeof(sbuf), "cannot connect: " "monetdbd server requires unknown hash: %s", shash); close_stream(fdout); close_stream(fdin); return(strdup(sbuf)); } /* now hash the password hash with the provided * challenge */ for (; *algs != NULL; algs++) { /* TODO: make this actually obey the separation by * commas, and only allow full matches */ if (strstr(algos, *algs) != NULL) { p = mcrypt_hashPassword(*algs, phash, chal); if (p == NULL) continue; mnstr_printf(fdout, "BIG:monetdb:{%s}%s:control:merovingian:\n", *algs, p); mnstr_flush(fdout); free(p); break; } } free(phash); if (p == NULL) { /* the server doesn't support what we can */ snprintf(sbuf, sizeof(sbuf), "cannot connect: " "unsupported hash algoritms: %s", algos); close_stream(fdout); close_stream(fdin); return(strdup(sbuf)); } } } if (fdin != NULL) { /* stream.h is sooo broken :( */ memset(rbuf, '\0', sizeof(rbuf)); if ((len = mnstr_read_block(fdin, rbuf, sizeof(rbuf) - 1, 1)) < 0) { close_stream(fdout); close_stream(fdin); return(strdup("no response from monetdbd after login")); } rbuf[len - 1] = '\0'; } else { if ((len = recv(sock, rbuf, sizeof(rbuf), 0)) <= 0) { close(sock); return(strdup("no response from monetdbd after login")); } rbuf[len - 1] = '\0'; } if (strcmp(rbuf, "=OK") != 0 && strcmp(rbuf, "OK") != 0) { buf = rbuf; if (*buf == '!') buf++; if (fdin != NULL) { close_stream(fdout); close_stream(fdin); } else { close(sock); } return(strdup(buf)); } } if (fdout != NULL) { mnstr_printf(fdout, "%s %s\n", database, command); mnstr_flush(fdout); } else { len = snprintf(sbuf, sizeof(sbuf), "%s %s\n", database, command); if (send(sock, sbuf, len, 0) == -1) { close(sock); return(strdup("failed to send control command to server")); } } if (wait != 0) { size_t buflen = sizeof(sbuf); size_t bufpos = 0; char *bufp; bufp = buf = malloc(sizeof(char) * buflen); if (buf == NULL) { if (fdin != NULL) { close_stream(fdin); close_stream(fdout); } else { close(sock); } return(strdup("failed to allocate memory")); } while (1) { if (fdin != NULL) { /* stream.h is sooo broken :( */ memset(buf + bufpos, '\0', buflen - bufpos); len = mnstr_read_block(fdin, buf + bufpos, buflen - bufpos - 1, 1); if (len >= 0) len = strlen(buf + bufpos); } else { len = recv(sock, buf + bufpos, buflen - bufpos, 0); } if (len <= 0) break; if ((size_t)len == buflen - bufpos) { buflen *= 2; bufp = realloc(buf, sizeof(char) * buflen); if (bufp == NULL) { free(buf); if (fdin != NULL) { close_stream(fdin); close_stream(fdout); } else { close(sock); } return(strdup("failed to allocate more memory")); } buf = bufp; } bufpos += (size_t)len; } if (bufpos == 0) { if (fdin != NULL) { close_stream(fdin); close_stream(fdout); } else { close(sock); } free(buf); return(strdup("incomplete response from monetdbd")); } buf[bufpos - 1] = '\0'; if (fdin) { /* strip out protocol = */ memmove(bufp, bufp + 1, strlen(bufp + 1) + 1); while ((bufp = strstr(bufp, "\n=")) != NULL) memmove(bufp + 1, bufp + 2, strlen(bufp + 2) + 1); } *ret = buf; } else { if (fdin != NULL) { if (mnstr_read_block(fdin, rbuf, sizeof(rbuf) - 1, 1) < 0) { close_stream(fdin); close_stream(fdout); return(strdup("incomplete response from monetdbd")); } rbuf[strlen(rbuf) - 1] = '\0'; *ret = strdup(rbuf + 1); } else { if ((len = recv(sock, rbuf, sizeof(rbuf), 0)) <= 0) { close(sock); return(strdup("incomplete response from monetdbd")); } rbuf[len - 1] = '\0'; *ret = strdup(rbuf); } } if (fdin != NULL) { close_stream(fdin); close_stream(fdout); } else { close(sock); } return(NULL); }
/* * Input to be processed is collected in a Client specific buffer. It * is filled by reading information from a stream, a terminal, or by * scheduling strings constructed internally. The latter involves * removing any escape character needed to manipulate the string within * the kernel. The buffer space is automatically expanded to * accommodate new information and the read pointers are adjusted. * * The input is read from a (blocked) stream and stored in the client * record input buffer. The storage area grows automatically upon need. * The origin of the input stream depends on the connectivity mode. * * Each operation received from a front-end consists of at least one * line. To simplify misaligned communication with front-ends, we use * different prompts structures. * * The default action is to read information from an ascii-stream one * line at a time. This is the preferred mode for reading from terminal. * * The next statement block is to be read. Send a prompt to warn the * front-end to issue the request. */ int MCreadClient(Client c) { bstream *in = c->fdin; #ifdef MAL_CLIENT_DEBUG printf("# streamClient %d %d\n", c->idx, isa_block_stream(in->s)); #endif while (in->pos < in->len && (isspace((int) (in->buf[in->pos])) || in->buf[in->pos] == ';' || !in->buf[in->pos])) in->pos++; if (in->pos >= in->len || in->mode) { ssize_t rd, sum = 0; if (in->eof || !isa_block_stream(c->fdout)) { if (!isa_block_stream(c->fdout) && c->promptlength > 0) mnstr_write(c->fdout, c->prompt, c->promptlength, 1); mnstr_flush(c->fdout); in->eof = 0; } while ((rd = bstream_next(in)) > 0 && !in->eof) { sum += rd; if (!in->mode) /* read one line at a time in line mode */ break; } if (in->mode) { /* find last new line */ char *p = in->buf + in->len - 1; while (p > in->buf && *p != '\n') { *(p + 1) = *p; p--; } if (p > in->buf) *(p + 1) = 0; if (p != in->buf + in->len - 1) in->len++; } if (sum == 0 && in->eof && isa_block_stream(in->s)) { /* we hadn't seen the EOF before, so just try again (this time with prompt) */ #ifdef MAL_CLIENT_DEBUG printf("# retry stream reading %d %d\n", c->idx, in->eof); #endif return MCreadClient(c); } #ifdef MAL_CLIENT_DEBUG printf("# simple stream received %d sum " SZFMT "\n", c->idx, sum); #endif } if (in->pos >= in->len) { /* end of stream reached */ #ifdef MAL_CLIENT_DEBUG printf("# end of stream received %d %d\n", c->idx, c->bak == 0); #endif if (c->bak) { MCpopClientInput(c); if (c->fdin == NULL) return 0; return MCreadClient(c); } return 0; } if (*CURRENT(c) == '?') { showHelp(c->nspace, CURRENT(c) + 1, c->fdout); in->pos = in->len; return MCreadClient(c); } #ifdef MAL_CLIENT_DEBUG printf("# finished stream read %d %d\n", (int) in->pos, (int) in->len); printf("#%s\n", in->buf); #endif return 1; }
/* #define _SQL_READER_DEBUG */ str SQLreader(Client c) { int go = TRUE; int more = TRUE; int commit_done = FALSE; backend *be = (backend *) c->sqlcontext; bstream *in = c->fdin; int language = -1; mvc *m = NULL; int blocked = isa_block_stream(in->s); if (SQLinitialized == FALSE) { c->mode = FINISHCLIENT; return NULL; } if (!be || c->mode <= FINISHCLIENT) { #ifdef _SQL_READER_DEBUG mnstr_printf(GDKout, "#SQL client finished\n"); #endif c->mode = FINISHCLIENT; return NULL; } #ifdef _SQL_READER_DEBUG mnstr_printf(GDKout, "#SQLparser: start reading SQL %s %s\n", (be->console ? " from console" : ""), (blocked ? "Blocked read" : "")); #endif language = be->language; /* 'S' for SQL, 'D' from debugger */ m = be->mvc; m->errstr[0] = 0; /* * Continue processing any left-over input from the previous round. */ #ifdef _SQL_READER_DEBUG mnstr_printf(GDKout, "#pos %d len %d eof %d \n", in->pos, in->len, in->eof); #endif /* * Distinguish between console reading and mclient connections. * The former comes with readline functionality. */ while (more) { more = FALSE; /* Different kinds of supported statements sequences A; -- single line s A \n B; -- multi line S A; B; -- compound single block s A; -- many multi line B \n C; -- statements in one block S */ /* auto_commit on end of statement */ if (m->scanner.mode == LINE_N && !commit_done) { go = SQLautocommit(c, m); commit_done = TRUE; } if (go && in->pos >= in->len) { ssize_t rd; if (c->bak) { #ifdef _SQL_READER_DEBUG mnstr_printf(GDKout, "#Switch to backup stream\n"); #endif in = c->fdin; blocked = isa_block_stream(in->s); m->scanner.rs = c->fdin; c->fdin->pos += c->yycur; c->yycur = 0; } if (in->eof || !blocked) { language = (be->console) ? 'S' : 0; /* The rules of auto_commit require us to finish and start a transaction on the start of a new statement (s A;B; case) */ if (!(m->emod & mod_debug) && !commit_done) { go = SQLautocommit(c, m); commit_done = TRUE; } if (go && ((!blocked && mnstr_write(c->fdout, c->prompt, c->promptlength, 1) != 1) || mnstr_flush(c->fdout))) { go = FALSE; break; } in->eof = 0; } if (in->buf == NULL) { more = FALSE; go = FALSE; } else if (go && (rd = bstream_next(in)) <= 0) { #ifdef _SQL_READER_DEBUG mnstr_printf(GDKout, "#rd %d language %d eof %d\n", rd, language, in->eof); #endif if (be->language == 'D' && in->eof == 0) return 0; if (rd == 0 && language !=0 && in->eof && !be->console) { /* we hadn't seen the EOF before, so just try again (this time with prompt) */ more = TRUE; continue; } go = FALSE; break; } else if (go && !be->console && language == 0) { if (in->buf[in->pos] == 's' && !in->eof) { while ((rd = bstream_next(in)) > 0) ; } be->language = in->buf[in->pos++]; if (be->language == 's') { be->language = 'S'; m->scanner.mode = LINE_1; } else if (be->language == 'S') { m->scanner.mode = LINE_N; } } #ifdef _SQL_READER_DEBUG mnstr_printf(GDKout, "#SQL blk:%s\n", in->buf + in->pos); #endif } } if ( (c->stimeout && (GDKusec() - c->session) > c->stimeout) || !go || (strncmp(CURRENT(c), "\\q", 2) == 0)) { in->pos = in->len; /* skip rest of the input */ c->mode = FINISHCLIENT; return NULL; } return 0; }
str SQLparser(Client c) { bstream *in = c->fdin; stream *out = c->fdout; str msg = NULL; backend *be; mvc *m; int oldvtop, oldstop; int pstatus = 0; int err = 0, opt = 0; be = (backend *) c->sqlcontext; if (be == 0) { /* tell the client */ mnstr_printf(out, "!SQL state descriptor missing, aborting\n"); mnstr_flush(out); /* leave a message in the log */ fprintf(stderr, "SQL state descriptor missing, cannot handle client!\n"); /* stop here, instead of printing the exception below to the * client in an endless loop */ c->mode = FINISHCLIENT; throw(SQL, "SQLparser", "State descriptor missing"); } oldvtop = c->curprg->def->vtop; oldstop = c->curprg->def->stop; be->vtop = oldvtop; #ifdef _SQL_PARSER_DEBUG mnstr_printf(GDKout, "#SQL compilation \n"); printf("debugger? %d(%d)\n", (int) be->mvc->emode, (int) be->mvc->emod); #endif m = be->mvc; m->type = Q_PARSE; SQLtrans(m); pstatus = m->session->status; /* sqlparse needs sql allocator to be available. It can be NULL at * this point if this is a recursive call. */ if (!m->sa) m->sa = sa_create(); m->emode = m_normal; m->emod = mod_none; if (be->language == 'X') { int n = 0, v, off, len; if (strncmp(in->buf + in->pos, "export ", 7) == 0) n = sscanf(in->buf + in->pos + 7, "%d %d %d", &v, &off, &len); if (n == 2 || n == 3) { mvc_export_chunk(be, out, v, off, n == 3 ? len : m->reply_size); in->pos = in->len; /* HACK: should use parsed length */ return MAL_SUCCEED; } if (strncmp(in->buf + in->pos, "close ", 6) == 0) { res_table *t; v = (int) strtol(in->buf + in->pos + 6, NULL, 0); t = res_tables_find(m->results, v); if (t) m->results = res_tables_remove(m->results, t); in->pos = in->len; /* HACK: should use parsed length */ return MAL_SUCCEED; } if (strncmp(in->buf + in->pos, "release ", 8) == 0) { cq *q = NULL; v = (int) strtol(in->buf + in->pos + 8, NULL, 0); if ((q = qc_find(m->qc, v)) != NULL) qc_delete(m->qc, q); in->pos = in->len; /* HACK: should use parsed length */ return MAL_SUCCEED; } if (strncmp(in->buf + in->pos, "auto_commit ", 12) == 0) { int commit; v = (int) strtol(in->buf + in->pos + 12, NULL, 10); commit = (!m->session->auto_commit && v); m->session->auto_commit = (v) != 0; m->session->ac_on_commit = m->session->auto_commit; if (m->session->active) { if (commit && mvc_commit(m, 0, NULL) < 0) { mnstr_printf(out, "!COMMIT: commit failed while " "enabling auto_commit\n"); msg = createException(SQL, "SQLparser", "Xauto_commit (commit) failed"); } else if (!commit && mvc_rollback(m, 0, NULL) < 0) { RECYCLEdrop(0); mnstr_printf(out, "!COMMIT: rollback failed while " "disabling auto_commit\n"); msg = createException(SQL, "SQLparser", "Xauto_commit (rollback) failed"); } } in->pos = in->len; /* HACK: should use parsed length */ if (msg != NULL) goto finalize; return MAL_SUCCEED; } if (strncmp(in->buf + in->pos, "reply_size ", 11) == 0) { v = (int) strtol(in->buf + in->pos + 11, NULL, 10); if (v < -1) { msg = createException(SQL, "SQLparser", "reply_size cannot be negative"); goto finalize; } m->reply_size = v; in->pos = in->len; /* HACK: should use parsed length */ return MAL_SUCCEED; } if (strncmp(in->buf + in->pos, "sizeheader", 10) == 0) { v = (int) strtol(in->buf + in->pos + 10, NULL, 10); m->sizeheader = v != 0; in->pos = in->len; /* HACK: should use parsed length */ return MAL_SUCCEED; } if (strncmp(in->buf + in->pos, "quit", 4) == 0) { c->mode = FINISHCLIENT; return MAL_SUCCEED; } mnstr_printf(out, "!unrecognized X command: %s\n", in->buf + in->pos); msg = createException(SQL, "SQLparser", "unrecognized X command"); goto finalize; } if (be->language !='S') { mnstr_printf(out, "!unrecognized language prefix: %ci\n", be->language); msg = createException(SQL, "SQLparser", "unrecognized language prefix: %c", be->language); goto finalize; } if ((err = sqlparse(m)) || /* Only forget old errors on transaction boundaries */ (mvc_status(m) && m->type != Q_TRANS) || !m->sym) { if (!err &&m->scanner.started) /* repeat old errors, with a parsed query */ err = mvc_status(m); if (err) { msg = createException(PARSE, "SQLparser", "%s", m->errstr); handle_error(m, c->fdout, pstatus); } sqlcleanup(m, err); goto finalize; } assert(m->session->schema != NULL); /* * We have dealt with the first parsing step and advanced the input reader * to the next statement (if any). * Now is the time to also perform the semantic analysis, optimize and * produce code. */ be->q = NULL; if (m->emode == m_execute) { assert(m->sym->data.lval->h->type == type_int); be->q = qc_find(m->qc, m->sym->data.lval->h->data.i_val); if (!be->q) { err = -1; mnstr_printf(out, "!07003!EXEC: no prepared statement with id: %d\n", m->sym->data.lval->h->data.i_val); msg = createException(SQL, "PREPARE", "no prepared statement with id: %d", m->sym->data.lval->h->data.i_val); handle_error(m, c->fdout, pstatus); sqlcleanup(m, err); goto finalize; } else if (be->q->type != Q_PREPARE) { err = -1; mnstr_printf(out, "!07005!EXEC: given handle id is not for a " "prepared statement: %d\n", m->sym->data.lval->h->data.i_val); msg = createException(SQL, "PREPARE", "is not a prepared statement: %d", m->sym->data.lval->h->data.i_val); handle_error(m, c->fdout, pstatus); sqlcleanup(m, err); goto finalize; } m->emode = m_inplace; scanner_query_processed(&(m->scanner)); } else if (caching(m) && cachable(m, NULL) && m->emode != m_prepare && (be->q = qc_match(m->qc, m->sym, m->args, m->argc, m->scanner.key ^ m->session->schema->base.id)) != NULL) { // look for outdated plans if ( OPTmitosisPlanOverdue(c, be->q->name) ){ msg = SQLCacheRemove(c, be->q->name); qc_delete(be->mvc->qc, be->q); goto recompilequery; } if (m->emod & mod_debug) SQLsetDebugger(c, m, TRUE); if (m->emod & mod_trace) SQLsetTrace(be, c, TRUE); if (!(m->emod & (mod_explain | mod_debug | mod_trace | mod_dot))) m->emode = m_inplace; scanner_query_processed(&(m->scanner)); } else { sql_rel *r; stmt *s; recompilequery: r = sql_symbol2relation(m, m->sym); s = sql_relation2stmt(m, r); if (s == 0 || (err = mvc_status(m) && m->type != Q_TRANS)) { msg = createException(PARSE, "SQLparser", "%s", m->errstr); handle_error(m, c->fdout, pstatus); sqlcleanup(m, err); goto finalize; } assert(s); /* generate the MAL code */ if (m->emod & mod_trace) SQLsetTrace(be, c, TRUE); if (m->emod & mod_debug) SQLsetDebugger(c, m, TRUE); if (!caching(m) || !cachable(m, s)) { scanner_query_processed(&(m->scanner)); if (backend_callinline(be, c, s, 0) == 0) { opt = 1; } else { err = 1; } } else { /* generate a factory instantiation */ be->q = qc_insert(m->qc, m->sa, /* the allocator */ r, /* keep relational query */ m->sym, /* the sql symbol tree */ m->args, /* the argument list */ m->argc, m->scanner.key ^ m->session->schema->base.id, /* the statement hash key */ m->emode == m_prepare ? Q_PREPARE : m->type, /* the type of the statement */ sql_escape_str(QUERY(m->scanner))); scanner_query_processed(&(m->scanner)); be->q->code = (backend_code) backend_dumpproc(be, c, be->q, s); if (!be->q->code) err = 1; be->q->stk = 0; /* passed over to query cache, used during dumpproc */ m->sa = NULL; m->sym = NULL; /* register name in the namespace */ be->q->name = putName(be->q->name, strlen(be->q->name)); if (m->emode == m_normal && m->emod == mod_none) m->emode = m_inplace; } } if (err) m->session->status = -10; if (err == 0) { if (be->q) { if (m->emode == m_prepare) err = mvc_export_prepare(m, c->fdout, be->q, ""); else if (m->emode == m_inplace) { /* everything ready for a fast call */ } else { /* call procedure generation (only in cache mode) */ backend_call(be, c, be->q); } } /* In the final phase we add any debugging control */ if (m->emod & mod_trace) SQLsetTrace(be, c, FALSE); if (m->emod & mod_debug) SQLsetDebugger(c, m, FALSE); /* * During the execution of the query exceptions can be raised. * The default action is to print them out at the end of the * query block. */ pushEndInstruction(c->curprg->def); chkTypes(c->fdout, c->nspace, c->curprg->def, TRUE); /* resolve types */ if (opt) { MalBlkPtr mb = c->curprg->def; trimMalBlk(mb); chkProgram(c->fdout, c->nspace, mb); addOptimizers(c, mb, "default_pipe"); msg = optimizeMALBlock(c, mb); if (msg != MAL_SUCCEED) { sqlcleanup(m, err); goto finalize; } c->curprg->def = mb; } //printFunction(c->fdout, c->curprg->def, 0, LIST_MAL_ALL); /* we know more in this case than chkProgram(c->fdout, c->nspace, c->curprg->def); */ if (c->curprg->def->errors) { showErrors(c); /* restore the state */ MSresetInstructions(c->curprg->def, oldstop); freeVariables(c, c->curprg->def, c->glb, oldvtop); c->curprg->def->errors = 0; msg = createException(PARSE, "SQLparser", "Semantic errors"); } } finalize: if (msg) sqlcleanup(m, 0); return msg; }
int main(int argc, char **argv) { int port = 0; char *user = NULL; char *passwd = NULL; char *host = NULL; char *dbname = NULL; int trace = 0; int describe = 0; int functions = 0; int useinserts = 0; int c; Mapi mid; int quiet = 0; stream *out; char user_set_as_flag = 0; char *table = NULL; static struct option long_options[] = { {"host", 1, 0, 'h'}, {"port", 1, 0, 'p'}, {"database", 1, 0, 'd'}, {"describe", 0, 0, 'D'}, {"functions", 0, 0, 'f'}, {"table", 1, 0, 't'}, {"inserts", 0, 0, 'N'}, {"Xdebug", 0, 0, 'X'}, {"user", 1, 0, 'u'}, {"quiet", 0, 0, 'q'}, {"help", 0, 0, '?'}, {0, 0, 0, 0} }; parse_dotmonetdb(&user, &passwd, NULL, NULL, NULL, NULL); while ((c = getopt_long(argc, argv, "h:p:d:Dft:NXu:q?", long_options, NULL)) != -1) { switch (c) { case 'u': if (user) free(user); user = strdup(optarg); user_set_as_flag = 1; break; case 'h': host = optarg; break; case 'p': assert(optarg != NULL); port = atoi(optarg); break; case 'd': dbname = optarg; break; case 'D': describe = 1; break; case 'N': useinserts = 1; break; case 'f': if (table) usage(argv[0], -1); functions = 1; break; case 't': if (table || functions) usage(argv[0], -1); table = optarg; break; case 'q': quiet = 1; break; case 'X': trace = MAPI_TRACE; break; case '?': /* a bit of a hack: look at the option that the current `c' is based on and see if we recognize it: if -? or --help, exit with 0, else with -1 */ usage(argv[0], strcmp(argv[optind - 1], "-?") == 0 || strcmp(argv[optind - 1], "--help") == 0 ? 0 : -1); default: usage(argv[0], -1); } } if (optind == argc - 1) dbname = argv[optind]; else if (optind != argc) usage(argv[0], -1); /* when config file would provide defaults */ if (user_set_as_flag) passwd = NULL; if (user == NULL) user = simple_prompt("user", BUFSIZ, 1, prompt_getlogin()); if (passwd == NULL) passwd = simple_prompt("password", BUFSIZ, 0, NULL); mid = mapi_connect(host, port, user, passwd, "sql", dbname); if (user) free(user); if (passwd) free(passwd); if (mid == NULL) { fprintf(stderr, "failed to allocate Mapi structure\n"); exit(2); } if (mapi_error(mid)) { mapi_explain(mid, stderr); exit(2); } if (!quiet) { char *motd = mapi_get_motd(mid); if (motd) fprintf(stderr, "%s", motd); } mapi_trace(mid, trace); mapi_cache_limit(mid, 10000); out = file_wastream(stdout, "stdout"); if (out == NULL) { fprintf(stderr, "failed to allocate stream\n"); exit(2); } if (!quiet) { char buf[27]; time_t t = time(0); char *p; #ifdef HAVE_CTIME_R3 ctime_r(&t, buf, sizeof(buf)); #else #ifdef HAVE_CTIME_R ctime_r(&t, buf); #else strncpy(buf, ctime(&t), sizeof(buf)); #endif #endif if ((p = strrchr(buf, '\n')) != NULL) *p = 0; mnstr_printf(out, "-- msqldump %s %s%s %s\n", describe ? "describe" : "dump", functions ? "functions" : table ? "table " : "database", table ? table : "", buf); dump_version(mid, out, "--"); } if (functions) c = dump_functions(mid, out, NULL, NULL); else if (table) c = dump_table(mid, NULL, table, out, describe, 1, useinserts); else c = dump_database(mid, out, describe, useinserts); mnstr_flush(out); mapi_destroy(mid); if (mnstr_errnr(out)) { fprintf(stderr, "%s: %s", argv[0], mnstr_error(out)); return 1; } mnstr_destroy(out); return c; }
void MSscheduleClient(str command, str challenge, bstream *fin, stream *fout) { char *user = command, *algo = NULL, *passwd = NULL, *lang = NULL; char *database = NULL, *s, *dbname; Client c; /* decode BIG/LIT:user:{cypher}passwordchal:lang:database: line */ /* byte order */ s = strchr(user, ':'); if (s) { *s = 0; mnstr_set_byteorder(fin->s, strcmp(user, "BIG") == 0); user = s + 1; } else { mnstr_printf(fout, "!incomplete challenge '%s'\n", user); exit_streams(fin, fout); GDKfree(command); return; } /* passwd */ s = strchr(user, ':'); if (s) { *s = 0; passwd = s + 1; /* decode algorithm, i.e. {plain}mypasswordchallenge */ if (*passwd != '{') { mnstr_printf(fout, "!invalid password entry\n"); exit_streams(fin, fout); GDKfree(command); return; } algo = passwd + 1; s = strchr(algo, '}'); if (!s) { mnstr_printf(fout, "!invalid password entry\n"); exit_streams(fin, fout); GDKfree(command); return; } *s = 0; passwd = s + 1; } else { mnstr_printf(fout, "!incomplete challenge '%s'\n", user); exit_streams(fin, fout); GDKfree(command); return; } /* lang */ s = strchr(passwd, ':'); if (s) { *s = 0; lang = s + 1; } else { mnstr_printf(fout, "!incomplete challenge, missing language\n"); exit_streams(fin, fout); GDKfree(command); return; } /* database */ s = strchr(lang, ':'); if (s) { *s = 0; database = s + 1; /* we can have stuff following, make it void */ s = strchr(database, ':'); if (s) *s = 0; } dbname = GDKgetenv("gdk_dbname"); if (database != NULL && database[0] != '\0' && strcmp(database, dbname) != 0) { mnstr_printf(fout, "!request for database '%s', " "but this is database '%s', " "did you mean to connect to monetdbd instead?\n", database, dbname); /* flush the error to the client, and abort further execution */ exit_streams(fin, fout); GDKfree(command); return; } else { str err; oid uid; sabdb *stats = NULL; Client root = &mal_clients[0]; /* access control: verify the credentials supplied by the user, * no need to check for database stuff, because that is done per * database itself (one gets a redirect) */ err = AUTHcheckCredentials(&uid, root, &user, &passwd, &challenge, &algo); if (err != MAL_SUCCEED) { mnstr_printf(fout, "!%s\n", err); exit_streams(fin, fout); GDKfree(err); GDKfree(command); return; } err = SABAOTHgetMyStatus(&stats); if (err != MAL_SUCCEED) { /* this is kind of awful, but we need to get rid of this * message */ fprintf(stderr, "!SABAOTHgetMyStatus: %s\n", err); if (err != M5OutOfMemory) GDKfree(err); mnstr_printf(fout, "!internal server error, " "please try again later\n"); exit_streams(fin, fout); GDKfree(command); return; } if (stats->locked == 1) { if (uid == 0) { mnstr_printf(fout, "#server is running in " "maintenance mode\n"); } else { mnstr_printf(fout, "!server is running in " "maintenance mode, please try again later\n"); exit_streams(fin, fout); SABAOTHfreeStatus(&stats); GDKfree(command); return; } } SABAOTHfreeStatus(&stats); c = MCinitClient(uid, fin, fout); if (c == NULL) { if ( MCshutdowninprogress()) mnstr_printf(fout, "!system shutdown in progress, please try again later\n"); else mnstr_printf(fout, "!maximum concurrent client limit reached " "(%d), please try again later\n", MAL_MAXCLIENTS); exit_streams(fin, fout); GDKfree(command); return; } /* move this back !! */ if (c->nspace == 0) { c->nspace = newModule(NULL, putName("user", 4)); c->nspace->outer = mal_clients[0].nspace->outer; } if ((s = setScenario(c, lang)) != NULL) { mnstr_printf(c->fdout, "!%s\n", s); mnstr_flush(c->fdout); GDKfree(s); c->mode = FINISHCLIENT; } if (!GDKgetenv_isyes(mal_enableflag) && (strncasecmp("sql", lang, 3) != 0 && uid != 0)) { mnstr_printf(fout, "!only the 'monetdb' user can use non-sql languages. " "run mserver5 with --set %s=yes to change this.\n", mal_enableflag); exit_streams(fin, fout); GDKfree(command); return; } } MSinitClientPrg(c, "user", "main"); GDKfree(command); /* NOTE ABOUT STARTING NEW THREADS * At this point we have conducted experiments (Jun 2012) with * reusing threads. The implementation used was a lockless array of * semaphores to wake up threads to do work. Experimentation on * Linux, Solaris and Darwin showed no significant improvements, in * most cases no improvements at all. Hence the following * conclusion: thread reuse doesn't save up on the costs of just * forking new threads. Since the latter means no difficulties of * properly maintaining a pool of threads and picking the workers * out of them, it is favourable just to start new threads on * demand. */ /* fork a new thread to handle this client */ mnstr_settimeout(c->fdin->s, 50, GDKexiting); MSserveClient(c); }
/* * Input to be processed is collected in a Client specific buffer. It * is filled by reading information from a stream, a terminal, or by * scheduling strings constructed internally. The latter involves * removing any escape character needed to manipulate the string within * the kernel. The buffer space is automatically expanded to * accommodate new information and the read pointers are adjusted. * * The input is read from a (blocked) stream and stored in the client * record input buffer. The storage area grows automatically upon need. * The origin of the input stream depends on the connectivity mode. * * Each operation received from a front-end consists of at least one * line. To simplify misaligned communication with front-ends, we use * different prompts structures. * * The default action is to read information from an ascii-stream one * line at a time. This is the preferred mode for reading from terminal. * * The next statement block is to be read. Send a prompt to warn the * front-end to issue the request. */ int MCreadClient(Client c) { bstream *in = c->fdin; #ifdef MAL_CLIENT_DEBUG fprintf(stderr,"# streamClient %d %d\n", c->idx, isa_block_stream(in->s)); #endif while (in->pos < in->len && (isspace((unsigned char) (in->buf[in->pos])) || in->buf[in->pos] == ';' || !in->buf[in->pos])) in->pos++; if (in->pos >= in->len || in->mode) { ssize_t rd, sum = 0; if (in->eof || !isa_block_stream(c->fdout)) { if (!isa_block_stream(c->fdout) && c->promptlength > 0) mnstr_write(c->fdout, c->prompt, c->promptlength, 1); mnstr_flush(c->fdout); in->eof = false; } while ((rd = bstream_next(in)) > 0 && !in->eof) { sum += rd; if (!in->mode) /* read one line at a time in line mode */ break; } if (in->mode) { /* find last new line */ char *p = in->buf + in->len - 1; while (p > in->buf && *p != '\n') { *(p + 1) = *p; p--; } if (p > in->buf) *(p + 1) = 0; if (p != in->buf + in->len - 1) in->len++; } #ifdef MAL_CLIENT_DEBUG fprintf(stderr, "# simple stream received %d sum %zu\n", c->idx, sum); #endif } if (in->pos >= in->len) { /* end of stream reached */ #ifdef MAL_CLIENT_DEBUG fprintf(stderr,"# end of stream received %d %d\n", c->idx, c->bak == 0); #endif if (c->bak) { MCpopClientInput(c); if (c->fdin == NULL) return 0; return MCreadClient(c); } return 0; } #ifdef MAL_CLIENT_DEBUG fprintf(stderr,"# finished stream read %d %d\n", (int) in->pos, (int) in->len); printf("#%s\n", in->buf); #endif return 1; }