📄 psqlscan.l
字号:
/* * These rules are specific to psql --- they implement parenthesis * counting and detection of command-ending semicolon. These must * appear before the {self} rule so that they take precedence over it. */"(" { cur_state->paren_depth++; ECHO; }")" { if (cur_state->paren_depth > 0) cur_state->paren_depth--; ECHO; }";" { ECHO; if (cur_state->paren_depth == 0) { /* Terminate lexing temporarily */ return LEXRES_SEMI; } } /* * psql-specific rules to handle backslash commands and variable * substitution. We want these before {self}, also. */"\\"[;:] { /* Force a semicolon or colon into the query buffer */ emit(yytext + 1, 1); }"\\" { /* Terminate lexing temporarily */ return LEXRES_BACKSLASH; }:[A-Za-z0-9_]+ { /* Possible psql variable substitution */ const char *value; value = GetVariable(pset.vars, yytext + 1); if (value) { /* It is a variable, perform substitution */ push_new_buffer(value); /* yy_scan_string already made buffer active */ } else { /* * if the variable doesn't exist we'll copy the * string as is */ ECHO; } } /* * Back to backend-compatible rules. */{self} { ECHO; }{operator} { /* * Check for embedded slash-star or dash-dash; those * are comment starts, so operator must stop there. * Note that slash-star or dash-dash at the first * character will match a prior rule, not this one. */ int nchars = yyleng; char *slashstar = strstr(yytext, "/*"); char *dashdash = strstr(yytext, "--"); if (slashstar && dashdash) { /* if both appear, take the first one */ if (slashstar > dashdash) slashstar = dashdash; } else if (!slashstar) slashstar = dashdash; if (slashstar) nchars = slashstar - yytext; /* * For SQL compatibility, '+' and '-' cannot be the * last char of a multi-char operator unless the operator * contains chars that are not in SQL operators. * The idea is to lex '=-' as two operators, but not * to forbid operator names like '?-' that could not be * sequences of SQL operators. */ while (nchars > 1 && (yytext[nchars-1] == '+' || yytext[nchars-1] == '-')) { int ic; for (ic = nchars-2; ic >= 0; ic--) { if (strchr("~!@#^&|`?%", yytext[ic])) break; } if (ic >= 0) break; /* found a char that makes it OK */ nchars--; /* else remove the +/-, and check again */ } if (nchars < yyleng) { /* Strip the unwanted chars from the token */ yyless(nchars); } ECHO; }{param} { ECHO; }{integer} { ECHO; }{decimal} { ECHO; }{real} { ECHO; }{realfail1} { /* * throw back the [Ee], and treat as {decimal}. Note * that it is possible the input is actually {integer}, * but since this case will almost certainly lead to a * syntax error anyway, we don't bother to distinguish. */ yyless(yyleng-1); ECHO; }{realfail2} { /* throw back the [Ee][+-], and proceed as above */ yyless(yyleng-2); ECHO; }{identifier} { ECHO; }{other} { ECHO; } /* * Everything from here down is psql-specific. */<<EOF>> { StackElem *stackelem = cur_state->buffer_stack; if (stackelem == NULL) return LEXRES_EOL; /* end of input reached */ /* * We were expanding a variable, so pop the inclusion * stack and keep lexing */ cur_state->buffer_stack = stackelem->next; yy_delete_buffer(stackelem->buf); free(stackelem->bufstring); if (stackelem->origstring) free(stackelem->origstring); free(stackelem); stackelem = cur_state->buffer_stack; if (stackelem != NULL) { yy_switch_to_buffer(stackelem->buf); cur_state->curline = stackelem->bufstring; cur_state->refline = stackelem->origstring ? stackelem->origstring : stackelem->bufstring; } else { yy_switch_to_buffer(cur_state->scanbufhandle); cur_state->curline = cur_state->scanbuf; cur_state->refline = cur_state->scanline; } } /* * Exclusive lexer states to handle backslash command lexing */<xslashcmd>{ /* command name ends at whitespace or backslash; eat all else */{space}|"\\" { yyless(0); return LEXRES_OK; }{other} { ECHO; }}<xslasharg>{ /* eat any whitespace, then decide what to do at first nonblank */{space}+ { }"\\" { /* * backslash is end of command or next command, do not eat * * XXX this means we can't conveniently accept options * that start with a backslash; therefore, option * processing that encourages use of backslashes is rather * broken. */ yyless(0); return LEXRES_OK; }{quote} { *option_quote = '\''; BEGIN(xslashquote); }"`" { if (option_type == OT_VERBATIM) { /* in verbatim mode, backquote is not special */ ECHO; BEGIN(xslashdefaultarg); } else { *option_quote = '`'; BEGIN(xslashbackquote); } }:[A-Za-z0-9_]* { /* Possible psql variable substitution */ if (option_type == OT_VERBATIM) ECHO; else { const char *value; value = GetVariable(pset.vars, yytext + 1); /* * The variable value is just emitted without any * further examination. This is consistent with the * pre-8.0 code behavior, if not with the way that * variables are handled outside backslash commands. */ if (value) appendPQExpBufferStr(output_buf, value); } *option_quote = ':'; return LEXRES_OK; }"|" { ECHO; if (option_type == OT_FILEPIPE) { /* treat like whole-string case */ BEGIN(xslashwholeline); } else { /* treat like default case */ BEGIN(xslashdefaultarg); } }{dquote} { *option_quote = '"'; ECHO; BEGIN(xslashquotedarg); }{other} { ECHO; BEGIN(xslashdefaultarg); }}<xslashquote>{ /* single-quoted text: copy literally except for backslash sequences */{quote} { return LEXRES_OK; }"\\n" { appendPQExpBufferChar(output_buf, '\n'); }"\\t" { appendPQExpBufferChar(output_buf, '\t'); }"\\b" { appendPQExpBufferChar(output_buf, '\b'); }"\\r" { appendPQExpBufferChar(output_buf, '\r'); }"\\f" { appendPQExpBufferChar(output_buf, '\f'); }{xqoctesc} { /* octal case */ appendPQExpBufferChar(output_buf, (char) strtol(yytext + 1, NULL, 8)); }{xqhexesc} { /* hex case */ appendPQExpBufferChar(output_buf, (char) strtol(yytext + 2, NULL, 16)); }"\\". { emit(yytext + 1, 1); }{other}|\n { ECHO; }}<xslashbackquote>{ /* * backticked text: copy everything until next backquote or end of line. * Invocation of the command will happen in psql_scan_slash_option. */"`" { return LEXRES_OK; }{other}|\n { ECHO; }}<xslashdefaultarg>{ /* * Copy everything until unquoted whitespace or end of line. Quotes * do not get stripped yet. */{space} { yyless(0); return LEXRES_OK; }"\\" { /* * unquoted backslash is end of command or next command, * do not eat * * (this was not the behavior pre-8.0, but it seems * consistent) */ yyless(0); return LEXRES_OK; }{dquote} { *option_quote = '"'; ECHO; BEGIN(xslashquotedarg); }{other} { ECHO; }}<xslashquotedarg>{ /* double-quoted text within a default-type argument: copy */{dquote} { ECHO; BEGIN(xslashdefaultarg); }{other}|\n { ECHO; }}<xslashwholeline>{ /* copy everything until end of input line */ /* but suppress leading whitespace */{space}+ { if (output_buf->len > 0) ECHO; }{other} { ECHO; }}<xslashend>{ /* at end of command, eat a double backslash, but not anything else */"\\\\" { return LEXRES_OK; }{other}|\n { yyless(0); return LEXRES_OK; }}%%/* * Create a lexer working state struct. */PsqlScanStatepsql_scan_create(void){ PsqlScanState state; state = (PsqlScanStateData *) pg_malloc_zero(sizeof(PsqlScanStateData)); psql_scan_reset(state); return state;}/* * Destroy a lexer working state struct, releasing all resources. */voidpsql_scan_destroy(PsqlScanState state){ psql_scan_finish(state); psql_scan_reset(state); free(state);}/* * Set up to perform lexing of the given input line. * * The text at *line, extending for line_len bytes, will be scanned by * subsequent calls to the psql_scan routines. psql_scan_finish should * be called when scanning is complete. Note that the lexer retains * a pointer to the storage at *line --- this string must not be altered * or freed until after psql_scan_finish is called. */voidpsql_scan_setup(PsqlScanState state, const char *line, int line_len){ /* Mustn't be scanning already */ psql_assert(state->scanbufhandle == NULL); psql_assert(state->buffer_stack == NULL); /* Do we need to hack the character set encoding? */ state->encoding = pset.encoding; state->safe_encoding = PG_VALID_BE_ENCODING(state->encoding); /* needed for prepare_buffer */ cur_state = state; /* Set up flex input buffer with appropriate translation and padding */ state->scanbufhandle = prepare_buffer(line, line_len, &state->scanbuf); state->scanline = line; /* Set lookaside data in case we have to map unsafe encoding */ state->curline = state->scanbuf; state->refline = state->scanline;}/* * Do lexical analysis of SQL command text. * * The text previously passed to psql_scan_setup is scanned, and appended * (possibly with transformation) to query_buf. * * The return value indicates the condition that stopped scanning: * * PSCAN_SEMICOLON: found a command-ending semicolon. (The semicolon is * transferred to query_buf.) The command accumulated in query_buf should * be executed, then clear query_buf and call again to scan the remainder * of the line. * * PSCAN_BACKSLASH: found a backslash that starts a psql special command. * Any previous data on the line has been transferred to query_buf. * The caller will typically next call psql_scan_slash_command(), * perhaps psql_scan_slash_option(), and psql_scan_slash_command_end(). * * PSCAN_INCOMPLETE: the end of the line was reached, but we have an * incomplete SQL command. *prompt is set to the appropriate prompt type. * * PSCAN_EOL: the end of the line was reached, and there is no lexical * reason to consider the command incomplete. The caller may or may not * choose to send it. *prompt is set to the appropriate prompt type if * the caller chooses to collect more input. * * In the PSCAN_INCOMPLETE and PSCAN_EOL cases, psql_scan_finish() should * be called next, then the cycle may be repeated with a fresh input line. * * In all cases, *prompt is set to an appropriate prompt type code for the * next line-input operation. */PsqlScanResultpsql_scan(PsqlScanState state, PQExpBuffer query_buf, promptStatus_t *prompt){ PsqlScanResult result; 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 = query_buf; if (state->buffer_stack != NULL) yy_switch_to_buffer(state->buffer_stack->buf); else yy_switch_to_buffer(state->scanbufhandle); BEGIN(state->start_state); /* And lex. */ lexresult = yylex(); /* Update static vars back to the state struct */ state->start_state = YY_START; /* * Check termination state and return appropriate result info. */ switch (lexresult)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -