📄 psqlscan.l
字号:
{ case LEXRES_EOL: /* end of input */ switch (state->start_state) { case INITIAL: if (state->paren_depth > 0) { result = PSCAN_INCOMPLETE; *prompt = PROMPT_PAREN; } else if (query_buf->len > 0) { result = PSCAN_EOL; *prompt = PROMPT_CONTINUE; } else { /* never bother to send an empty buffer */ result = PSCAN_INCOMPLETE; *prompt = PROMPT_READY; } break; case xb: result = PSCAN_INCOMPLETE; *prompt = PROMPT_SINGLEQUOTE; break; case xc: result = PSCAN_INCOMPLETE; *prompt = PROMPT_COMMENT; break; case xd: result = PSCAN_INCOMPLETE; *prompt = PROMPT_DOUBLEQUOTE; break; case xh: result = PSCAN_INCOMPLETE; *prompt = PROMPT_SINGLEQUOTE; break; case xq: result = PSCAN_INCOMPLETE; *prompt = PROMPT_SINGLEQUOTE; break; case xdolq: result = PSCAN_INCOMPLETE; *prompt = PROMPT_DOLLARQUOTE; break; default: /* can't get here */ fprintf(stderr, "invalid YY_START\n"); exit(1); } break; case LEXRES_SEMI: /* semicolon */ result = PSCAN_SEMICOLON; *prompt = PROMPT_READY; break; case LEXRES_BACKSLASH: /* backslash */ result = PSCAN_BACKSLASH; *prompt = PROMPT_READY; break; default: /* can't get here */ fprintf(stderr, "invalid yylex result\n"); exit(1); } return result;}/* * Clean up after scanning a string. This flushes any unread input and * releases resources (but not the PsqlScanState itself). Note however * that this does not reset the lexer scan state; that can be done by * psql_scan_reset(), which is an orthogonal operation. * * It is legal to call this when not scanning anything (makes it easier * to deal with error recovery). */voidpsql_scan_finish(PsqlScanState state){ /* Drop any incomplete variable expansions. */ while (state->buffer_stack != NULL) { StackElem *stackelem = state->buffer_stack; state->buffer_stack = stackelem->next; yy_delete_buffer(stackelem->buf); free(stackelem->bufstring); if (stackelem->origstring) free(stackelem->origstring); free(stackelem); } /* Done with the outer scan buffer, too */ if (state->scanbufhandle) yy_delete_buffer(state->scanbufhandle); state->scanbufhandle = NULL; if (state->scanbuf) free(state->scanbuf); state->scanbuf = NULL;}/* * Reset lexer scanning state to start conditions. This is appropriate * for executing \r psql commands (or any other time that we discard the * prior contents of query_buf). It is not, however, necessary to do this * when we execute and clear the buffer after getting a PSCAN_SEMICOLON or * PSCAN_EOL scan result, because the scan state must be INITIAL when those * conditions are returned. * * Note that this is unrelated to flushing unread input; that task is * done by psql_scan_finish(). */voidpsql_scan_reset(PsqlScanState state){ state->start_state = INITIAL; state->paren_depth = 0; state->xcdepth = 0; /* not really necessary */ if (state->dolqstart) free(state->dolqstart); state->dolqstart = NULL;}/* * Return true if lexer is currently in an "inside quotes" state. * * This is pretty grotty but is needed to preserve the old behavior * that mainloop.c drops blank lines not inside quotes without even * echoing them. */boolpsql_scan_in_quote(PsqlScanState state){ return state->start_state != INITIAL;}/* * Scan the command name of a psql backslash command. This should be called * after psql_scan() returns PSCAN_BACKSLASH. It is assumed that the input * has been consumed through the leading backslash. * * The return value is a malloc'd copy of the command name, as parsed off * from the input. */char *psql_scan_slash_command(PsqlScanState state){ PQExpBufferData mybuf; int lexresult; /* Must be scanning already */ psql_assert(state->scanbufhandle); /* Build a local buffer that we'll return the data of */ initPQExpBuffer(&mybuf); /* Set up static variables that will be used by yylex */ cur_state = state; output_buf = &mybuf; if (state->buffer_stack != NULL) yy_switch_to_buffer(state->buffer_stack->buf); else yy_switch_to_buffer(state->scanbufhandle); BEGIN(xslashcmd); /* And lex. */ lexresult = yylex(); /* There are no possible errors in this lex state... */ return mybuf.data;}/* * Parse off the next argument for a backslash command, and return it as a * malloc'd string. If there are no more arguments, returns NULL. * * type tells what processing, if any, to perform on the option string; * for example, if it's a SQL identifier, we want to downcase any unquoted * letters. * * if quote is not NULL, *quote is set to 0 if no quoting was found, else * the quote symbol. * * if semicolon is true, unquoted trailing semicolon(s) that would otherwise * be taken as part of the option string will be stripped. * * NOTE: the only possible syntax errors for backslash options are unmatched * quotes, which are detected when we run out of input. Therefore, on a * syntax error we just throw away the string and return NULL; there is no * need to worry about flushing remaining input. */char *psql_scan_slash_option(PsqlScanState state, enum slash_option_type type, char *quote, bool semicolon){ PQExpBufferData mybuf; int lexresult; char local_quote; bool badarg; /* Must be scanning already */ psql_assert(state->scanbufhandle); if (quote == NULL) quote = &local_quote; *quote = 0; /* Build a local buffer that we'll return the data of */ initPQExpBuffer(&mybuf); /* Set up static variables that will be used by yylex */ cur_state = state; output_buf = &mybuf; option_type = type; option_quote = quote; if (state->buffer_stack != NULL) yy_switch_to_buffer(state->buffer_stack->buf); else yy_switch_to_buffer(state->scanbufhandle); if (type == OT_WHOLE_LINE) BEGIN(xslashwholeline); else BEGIN(xslasharg); /* And lex. */ lexresult = yylex(); /* * Check the lex result: we should have gotten back either LEXRES_OK * or LEXRES_EOL (the latter indicating end of string). If we were inside * a quoted string, as indicated by YY_START, EOL is an error. */ psql_assert(lexresult == LEXRES_EOL || lexresult == LEXRES_OK); badarg = false; switch (YY_START) { case xslasharg: /* empty arg, or possibly a psql variable substitution */ break; case xslashquote: if (lexresult != LEXRES_OK) badarg = true; /* hit EOL not ending quote */ break; case xslashbackquote: if (lexresult != LEXRES_OK) badarg = true; /* hit EOL not ending quote */ else { /* Perform evaluation of backticked command */ char *cmd = mybuf.data; FILE *fd; bool error = false; PQExpBufferData output; char buf[512]; size_t result; fd = popen(cmd, PG_BINARY_R); if (!fd) { psql_error("%s: %s\n", cmd, strerror(errno)); error = true; } initPQExpBuffer(&output); if (!error) { do { result = fread(buf, 1, sizeof(buf), fd); if (ferror(fd)) { psql_error("%s: %s\n", cmd, strerror(errno)); error = true; break; } appendBinaryPQExpBuffer(&output, buf, result); } while (!feof(fd)); } if (fd && pclose(fd) == -1) { psql_error("%s: %s\n", cmd, strerror(errno)); error = true; } /* Now done with cmd, transfer result to mybuf */ resetPQExpBuffer(&mybuf); if (!error) { /* strip any trailing newline */ if (output.len > 0 && output.data[output.len - 1] == '\n') output.len--; appendBinaryPQExpBuffer(&mybuf, output.data, output.len); } termPQExpBuffer(&output); } break; case xslashdefaultarg: /* Strip any trailing semi-colons if requested */ if (semicolon) { while (mybuf.len > 0 && mybuf.data[mybuf.len - 1] == ';') { mybuf.data[--mybuf.len] = '\0'; } } /* * If SQL identifier processing was requested, then we strip out * excess double quotes and downcase unquoted letters. * Doubled double-quotes become output double-quotes, per spec. * * Note that a string like FOO"BAR"BAZ will be converted to * fooBARbaz; this is somewhat inconsistent with the SQL spec, * which would have us parse it as several identifiers. But * for psql's purposes, we want a string like "foo"."bar" to * be treated as one option, so there's little choice. */ if (type == OT_SQLID || type == OT_SQLIDHACK) { bool inquotes = false; char *cp = mybuf.data; while (*cp) { if (*cp == '"') { if (inquotes && cp[1] == '"') { /* Keep the first quote, remove the second */ cp++; } inquotes = !inquotes; /* Collapse out quote at *cp */ memmove(cp, cp + 1, strlen(cp)); mybuf.len--; /* do not advance cp */ } else { if (!inquotes && type == OT_SQLID) *cp = pg_tolower((unsigned char) *cp); cp += PQmblen(cp, pset.encoding); } } } break; case xslashquotedarg: /* must have hit EOL inside double quotes */ badarg = true; break; case xslashwholeline: /* always okay */ break; default: /* can't get here */ fprintf(stderr, "invalid YY_START\n"); exit(1); } if (badarg) { psql_error("unterminated quoted string\n"); termPQExpBuffer(&mybuf); return NULL; } /* * An unquoted empty argument isn't possible unless we are at end of * command. Return NULL instead. */ if (mybuf.len == 0 && *quote == 0) { termPQExpBuffer(&mybuf); return NULL; } /* Else return the completed string. */ return mybuf.data;}/* * Eat up any unused \\ to complete a backslash command. */voidpsql_scan_slash_command_end(PsqlScanState state){ int lexresult; /* Must be scanning already */ psql_assert(state->scanbufhandle); /* Set up static variables that will be used by yylex */ cur_state = state; output_buf = NULL; if (state->buffer_stack != NULL) yy_switch_to_buffer(state->buffer_stack->buf); else yy_switch_to_buffer(state->scanbufhandle); BEGIN(xslashend); /* And lex. */ lexresult = yylex(); /* There are no possible errors in this lex state... */}/* * "Push back" the passed string so that it will be rescanned by subsequent * psql_scan_slash_option calls. This is presently only used in the case * where a single-letter command has been concatenated with its argument. * * We use the same buffer stack mechanism as for variable expansion. */voidpsql_scan_slash_pushback(PsqlScanState state, const char *str){ /* needed for push_new_buffer */ cur_state = state; push_new_buffer(str);}/* * Push the given string onto the stack of stuff to scan. * * cur_state must point to the active PsqlScanState. * * NOTE SIDE EFFECT: the new buffer is made the active flex input buffer. */static voidpush_new_buffer(const char *newstr){ StackElem *stackelem; stackelem = (StackElem *) pg_malloc(sizeof(StackElem)); stackelem->buf = prepare_buffer(newstr, strlen(newstr), &stackelem->bufstring); cur_state->curline = stackelem->bufstring; if (cur_state->safe_encoding) { stackelem->origstring = NULL; cur_state->refline = stackelem->bufstring; } else { stackelem->origstring = pg_strdup(newstr); cur_state->refline = stackelem->origstring; } stackelem->next = cur_state->buffer_stack; cur_state->buffer_stack = stackelem;}/* * Set up a flex input buffer to scan the given data. We always make a * copy of the data. If working in an unsafe encoding, the copy has * multibyte sequences replaced by FFs to avoid fooling the lexer rules. * * cur_state must point to the active PsqlScanState. * * NOTE SIDE EFFECT: the new buffer is made the active flex input buffer. */static YY_BUFFER_STATEprepare_buffer(const char *txt, int len, char **txtcopy){ char *newtxt; /* Flex wants two \0 characters after the actual data */ newtxt = pg_malloc(len + 2); *txtcopy = newtxt; newtxt[len] = newtxt[len + 1] = YY_END_OF_BUFFER_CHAR; if (cur_state->safe_encoding) memcpy(newtxt, txt, len); else { /* Gotta do it the hard way */ int i = 0; while (i < len) { int thislen = PQmblen(txt + i, cur_state->encoding); /* first byte should always be okay... */ newtxt[i] = txt[i]; i++; while (--thislen > 0) newtxt[i++] = (char) 0xFF; } } return yy_scan_buffer(newtxt, len + 2);}/* * emit() --- body for ECHO macro * * NB: this must be used for ALL and ONLY the text copied from the flex * input data. If you pass it something that is not part of the yytext * string, you are making a mistake. Internally generated text can be * appended directly to output_buf. */static voidemit(const char *txt, int len){ if (cur_state->safe_encoding) appendBinaryPQExpBuffer(output_buf, txt, len); else { /* Gotta do it the hard way */ const char *reference = cur_state->refline; int i; reference += (txt - cur_state->curline); for (i = 0; i < len; i++) { char ch = txt[i]; if (ch == (char) 0xFF) ch = reference[i]; appendPQExpBufferChar(output_buf, ch); } }}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -