LCOV - code coverage report
Current view: top level - src/bin/psql - psqlscanslash.l (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 66.0 % 300 198
Test Date: 2026-02-28 03:14:59 Functions: 85.7 % 7 6
Legend: Lines:     hit not hit

            Line data    Source code
       1              : %top{
       2              : /*-------------------------------------------------------------------------
       3              :  *
       4              :  * psqlscanslash.l
       5              :  *    lexical scanner for psql backslash commands
       6              :  *
       7              :  * XXX Avoid creating backtracking cases --- see the backend lexer for info.
       8              :  *
       9              :  * See fe_utils/psqlscan_int.h for additional commentary.
      10              :  *
      11              :  * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
      12              :  * Portions Copyright (c) 1994, Regents of the University of California
      13              :  *
      14              :  * IDENTIFICATION
      15              :  *    src/bin/psql/psqlscanslash.l
      16              :  *
      17              :  *-------------------------------------------------------------------------
      18              :  */
      19              : #include "postgres_fe.h"
      20              : 
      21              : #include <ctype.h>
      22              : 
      23              : #include "common.h"
      24              : #include "psqlscanslash.h"
      25              : 
      26              : #include "common/logging.h"
      27              : #include "fe_utils/conditional.h"
      28              : 
      29              : #include "libpq-fe.h"
      30              : }
      31              : 
      32              : %{
      33              : #include "fe_utils/psqlscan_int.h"
      34              : 
      35              : /*
      36              :  * We must have a typedef YYSTYPE for yylex's first argument, but this lexer
      37              :  * doesn't presently make use of that argument, so just declare it as int.
      38              :  */
      39              : typedef int YYSTYPE;
      40              : 
      41              : /*
      42              :  * These variables do not need to be saved across calls.  Yeah, it's a bit
      43              :  * of a hack, but putting them into PsqlScanStateData would be klugy too.
      44              :  */
      45              : static enum slash_option_type option_type;
      46              : static char *option_quote;
      47              : static int  unquoted_option_chars;
      48              : static int  backtick_start_offset;
      49              : 
      50              : 
      51              : /* Return values from yylex() */
      52              : #define LEXRES_EOL          0   /* end of input */
      53              : #define LEXRES_OK           1   /* OK completion of backslash argument */
      54              : 
      55              : 
      56              : static void evaluate_backtick(PsqlScanState state);
      57              : 
      58              : #define ECHO psqlscan_emit(cur_state, yytext, yyleng)
      59              : 
      60              : /* LCOV_EXCL_START */
      61              : 
      62              : %}
      63              : 
      64              : /* Except for the prefix, these options should match psqlscan.l */
      65              : %option reentrant
      66              : %option bison-bridge
      67              : %option 8bit
      68              : %option never-interactive
      69              : %option nodefault
      70              : %option noinput
      71              : %option nounput
      72              : %option noyywrap
      73              : %option warn
      74              : %option prefix="slash_yy"
      75              : 
      76              : /*
      77              :  * Set the type of yyextra; we use it as a pointer back to the containing
      78              :  * PsqlScanState.
      79              :  */
      80              : %option extra-type="PsqlScanState"
      81              : 
      82              : /*
      83              :  * OK, here is a short description of lex/flex rules behavior.
      84              :  * The longest pattern which matches an input string is always chosen.
      85              :  * For equal-length patterns, the first occurring in the rules list is chosen.
      86              :  * INITIAL is the starting state, to which all non-conditional rules apply.
      87              :  * Exclusive states change parsing rules while the state is active.  When in
      88              :  * an exclusive state, only those rules defined for that state apply.
      89              :  */
      90              : 
      91              : /* Exclusive states for lexing backslash commands */
      92              : %x xslashcmd
      93              : %x xslashargstart
      94              : %x xslasharg
      95              : %x xslashquote
      96              : %x xslashbackquote
      97              : %x xslashdquote
      98              : %x xslashwholeline
      99              : %x xslashend
     100              : 
     101              : /*
     102              :  * Assorted character class definitions that should match psqlscan.l.
     103              :  */
     104              : space           [ \t\n\r\f\v]
     105              : quote           '
     106              : xeoctesc        [\\][0-7]{1,3}
     107              : xehexesc        [\\]x[0-9A-Fa-f]{1,2}
     108              : xqdouble        {quote}{quote}
     109              : dquote          \"
     110              : variable_char   [A-Za-z\200-\377_0-9]
     111              : 
     112              : other           .
     113              : 
     114              : %%
     115              : 
     116              : %{
     117              :         /* Declare some local variables inside yylex(), for convenience */
     118              :         PsqlScanState cur_state = yyextra;
     119       136348 :         PQExpBuffer output_buf = cur_state->output_buf;
     120       136348 : 
     121              :         /*
     122              :          * Force flex into the state indicated by start_state.  This has a
     123              :          * couple of purposes: it lets some of the functions below set a new
     124              :          * starting state without ugly direct access to flex variables, and it
     125              :          * allows us to transition from one flex lexer to another so that we
     126              :          * can lex different parts of the source string using separate lexers.
     127              :          */
     128              :         BEGIN(cur_state->start_state);
     129       136348 : %}
     130              : 
     131              :     /*
     132              :      * We don't really expect to be invoked in the INITIAL state in this
     133              :      * lexer; but if we are, just spit data to the output_buf until EOF.
     134              :      */
     135              : 
     136              : {other}|\n      { ECHO; }
     137            0 : 
     138            0 :     /*
     139              :      * Exclusive lexer states to handle backslash command lexing
     140              :      */
     141              : 
     142              : <xslashcmd>{
     143              :     /* command name ends at whitespace or backslash; eat all else */
     144        26097 : 
     145              : {space}|"\\"  {
     146              :                     yyless(0);
     147        26097 :                     cur_state->start_state = YY_START;
     148        26097 :                     return LEXRES_OK;
     149        26097 :                 }
     150              : 
     151              : {other}         { ECHO; }
     152       111625 : 
     153       111625 : }
     154              : 
     155              : <xslashargstart>{
     156              :     /*
     157              :      * Discard any whitespace before argument, then go to xslasharg state.
     158              :      * An exception is that "|" is only special at start of argument, so we
     159              :      * check for it here.
     160              :      */
     161        28685 : 
     162              : {space}+        { }
     163              : 
     164        28685 : "|"               {
     165           16 :                     if (option_type == OT_FILEPIPE)
     166           16 :                     {
     167              :                         /* treat like whole-string case */
     168              :                         ECHO;
     169            7 :                         BEGIN(xslashwholeline);
     170            7 :                     }
     171              :                     else
     172              :                     {
     173              :                         /* vertical bar is not special otherwise */
     174              :                         yyless(0);
     175            9 :                         BEGIN(xslasharg);
     176            9 :                     }
     177              :                 }
     178              : 
     179           16 : {other}         {
     180        29123 :                     yyless(0);
     181        29123 :                     BEGIN(xslasharg);
     182        29123 :                 }
     183              : 
     184        29123 : }
     185              : 
     186              : <xslasharg>{
     187              :     /*
     188              :      * Default processing of text in a slash command's argument.
     189              :      *
     190              :      * Note: unquoted_option_chars counts the number of characters at the
     191              :      * end of the argument that were not subject to any form of quoting.
     192              :      * psql_scan_slash_option needs this to strip trailing semicolons safely.
     193              :      */
     194         3738 : 
     195              : {space}|"\\"  {
     196              :                     /*
     197              :                      * Unquoted space is end of arg; do not eat.  Likewise
     198              :                      * backslash is end of command or next command, do not eat
     199              :                      *
     200              :                      * XXX this means we can't conveniently accept options
     201              :                      * that include unquoted backslashes; therefore, option
     202              :                      * processing that encourages use of backslashes is rather
     203              :                      * broken.
     204              :                      */
     205              :                     yyless(0);
     206         3738 :                     cur_state->start_state = YY_START;
     207         3738 :                     return LEXRES_OK;
     208         3738 :                 }
     209              : 
     210              : {quote}         {
     211        20062 :                     *option_quote = '\'';
     212        20062 :                     unquoted_option_chars = 0;
     213        20062 :                     BEGIN(xslashquote);
     214        20062 :                 }
     215              : 
     216        20062 : "`"               {
     217            9 :                     backtick_start_offset = output_buf->len;
     218            9 :                     *option_quote = '`';
     219            9 :                     unquoted_option_chars = 0;
     220            9 :                     BEGIN(xslashbackquote);
     221            9 :                 }
     222              : 
     223            9 : {dquote}        {
     224          747 :                     ECHO;
     225          747 :                     *option_quote = '"';
     226          747 :                     unquoted_option_chars = 0;
     227          747 :                     BEGIN(xslashdquote);
     228          747 :                 }
     229              : 
     230          747 : :{variable_char}+   {
     231          630 :                     /* Possible psql variable substitution */
     232              :                     if (cur_state->callbacks->get_variable == NULL)
     233          630 :                         ECHO;
     234            0 :                     else
     235              :                     {
     236              :                         char       *varname;
     237              :                         char       *value;
     238              : 
     239              :                         varname = psqlscan_extract_substring(cur_state,
     240          630 :                                                              yytext + 1,
     241          630 :                                                              yyleng - 1);
     242          630 :                         value = cur_state->callbacks->get_variable(varname,
     243          630 :                                                                    PQUOTE_PLAIN,
     244              :                                                                    cur_state->cb_passthrough);
     245              :                         free(varname);
     246          630 : 
     247              :                         /*
     248              :                          * The variable value is just emitted without any
     249              :                          * further examination.  This is consistent with the
     250              :                          * pre-8.0 code behavior, if not with the way that
     251              :                          * variables are handled outside backslash commands.
     252              :                          * Note that we needn't guard against recursion here.
     253              :                          */
     254              :                         if (value)
     255          630 :                         {
     256              :                             appendPQExpBufferStr(output_buf, value);
     257          612 :                             free(value);
     258          612 :                         }
     259              :                         else
     260              :                             ECHO;
     261           18 : 
     262              :                         *option_quote = ':';
     263          630 :                     }
     264              :                     unquoted_option_chars = 0;
     265          630 :                 }
     266              : 
     267          630 : :'{variable_char}+' {
     268           23 :                     psqlscan_escape_variable(cur_state, yytext, yyleng,
     269           23 :                                              PQUOTE_SQL_LITERAL);
     270              :                     *option_quote = ':';
     271           23 :                     unquoted_option_chars = 0;
     272           23 :                 }
     273              : 
     274           23 : 
     275           12 : :\"{variable_char}+\" {
     276              :                     psqlscan_escape_variable(cur_state, yytext, yyleng,
     277           12 :                                              PQUOTE_SQL_IDENT);
     278              :                     *option_quote = ':';
     279           12 :                     unquoted_option_chars = 0;
     280           12 :                 }
     281              : 
     282           12 : :\{\?{variable_char}+\} {
     283           10 :                     psqlscan_test_variable(cur_state, yytext, yyleng);
     284           10 :                 }
     285              : 
     286           10 : :'{variable_char}*  {
     287            0 :                     /* Throw back everything but the colon */
     288              :                     yyless(1);
     289            0 :                     unquoted_option_chars++;
     290            0 :                     ECHO;
     291            0 :                 }
     292              : 
     293            0 : :\"{variable_char}*    {
     294            0 :                     /* Throw back everything but the colon */
     295              :                     yyless(1);
     296            0 :                     unquoted_option_chars++;
     297            0 :                     ECHO;
     298            0 :                 }
     299              : 
     300            0 : :\{\?{variable_char}*   {
     301            0 :                     /* Throw back everything but the colon */
     302              :                     yyless(1);
     303            0 :                     unquoted_option_chars++;
     304            0 :                     ECHO;
     305            0 :                 }
     306              : 
     307            0 : :\{     {
     308            0 :                     /* Throw back everything but the colon */
     309              :                     yyless(1);
     310            0 :                     unquoted_option_chars++;
     311            0 :                     ECHO;
     312            0 :                 }
     313              : 
     314            0 : {other}         {
     315        69545 :                     unquoted_option_chars++;
     316        69545 :                     ECHO;
     317        69545 :                 }
     318              : 
     319        69545 : }
     320              : 
     321              : <xslashquote>{
     322              :     /*
     323              :      * single-quoted text: copy literally except for '' and backslash
     324              :      * sequences
     325              :      */
     326        20062 : 
     327              : {quote}         { BEGIN(xslasharg); }
     328        20062 : 
     329        20062 : {xqdouble}      { appendPQExpBufferChar(output_buf, '\''); }
     330           44 : 
     331           44 : "\\n"         { appendPQExpBufferChar(output_buf, '\n'); }
     332            3 : "\\t"         { appendPQExpBufferChar(output_buf, '\t'); }
     333            3 : "\\b"         { appendPQExpBufferChar(output_buf, '\b'); }
     334            3 : "\\r"         { appendPQExpBufferChar(output_buf, '\r'); }
     335            0 : "\\f"         { appendPQExpBufferChar(output_buf, '\f'); }
     336            3 : 
     337            0 : {xeoctesc}      {
     338            3 :                     /* octal case */
     339              :                     appendPQExpBufferChar(output_buf,
     340            3 :                                           (char) strtol(yytext + 1, NULL, 8));
     341            3 :                 }
     342              : 
     343            3 : {xehexesc}      {
     344            0 :                     /* hex case */
     345              :                     appendPQExpBufferChar(output_buf,
     346            0 :                                           (char) strtol(yytext + 2, NULL, 16));
     347            0 :                 }
     348              : 
     349            0 : "\\".         { psqlscan_emit(cur_state, yytext + 1, 1); }
     350            9 : 
     351            9 : {other}|\n      { ECHO; }
     352       713742 : 
     353       713742 : }
     354              : 
     355              : <xslashbackquote>{
     356              :     /*
     357              :      * backticked text: copy everything until next backquote (expanding
     358              :      * variable references, but doing nought else), then evaluate.
     359              :      */
     360            9 : 
     361              : "`"               {
     362              :                     /* In an inactive \if branch, don't evaluate the command */
     363              :                     if (cur_state->cb_passthrough == NULL ||
     364           18 :                         conditional_active((ConditionalStack) cur_state->cb_passthrough))
     365            9 :                         evaluate_backtick(cur_state);
     366            0 :                     BEGIN(xslasharg);
     367            9 :                 }
     368              : 
     369            9 : :{variable_char}+   {
     370            0 :                     /* Possible psql variable substitution */
     371              :                     if (cur_state->callbacks->get_variable == NULL)
     372            0 :                         ECHO;
     373            0 :                     else
     374              :                     {
     375              :                         char       *varname;
     376              :                         char       *value;
     377              : 
     378              :                         varname = psqlscan_extract_substring(cur_state,
     379            0 :                                                              yytext + 1,
     380            0 :                                                              yyleng - 1);
     381            0 :                         value = cur_state->callbacks->get_variable(varname,
     382            0 :                                                                    PQUOTE_PLAIN,
     383              :                                                                    cur_state->cb_passthrough);
     384              :                         free(varname);
     385            0 : 
     386              :                         if (value)
     387            0 :                         {
     388              :                             appendPQExpBufferStr(output_buf, value);
     389            0 :                             free(value);
     390            0 :                         }
     391              :                         else
     392              :                             ECHO;
     393            0 :                     }
     394              :                 }
     395              : 
     396            0 : :'{variable_char}+' {
     397            0 :                     psqlscan_escape_variable(cur_state, yytext, yyleng,
     398            0 :                                              PQUOTE_SHELL_ARG);
     399              :                 }
     400              : 
     401            0 : :'{variable_char}*  {
     402            0 :                     /* Throw back everything but the colon */
     403              :                     yyless(1);
     404            0 :                     ECHO;
     405            0 :                 }
     406              : 
     407            0 : {other}|\n      { ECHO; }
     408          117 : 
     409          117 : }
     410              : 
     411              : <xslashdquote>{
     412              :     /* double-quoted text: copy verbatim, including the double quotes */
     413          747 : 
     414              : {dquote}        {
     415              :                     ECHO;
     416          747 :                     BEGIN(xslasharg);
     417          747 :                 }
     418              : 
     419          747 : {other}|\n      { ECHO; }
     420        13791 : 
     421        13791 : }
     422              : 
     423              : <xslashwholeline>{
     424              :     /* copy everything until end of input line */
     425              :     /* but suppress leading whitespace */
     426          578 : 
     427              : {space}+        {
     428              :                     if (output_buf->len > 0)
     429          578 :                         ECHO;
     430          372 :                 }
     431              : 
     432          578 : {other}         { ECHO; }
     433         5668 : 
     434         5668 : }
     435              : 
     436              : <xslashend>{
     437              :     /* at end of command, eat a double backslash, but not anything else */
     438           35 : 
     439              : "\\\\"            {
     440              :                     cur_state->start_state = YY_START;
     441           35 :                     return LEXRES_OK;
     442           35 :                 }
     443              : 
     444              : {other}|\n      {
     445          455 :                     yyless(0);
     446          455 :                     cur_state->start_state = YY_START;
     447          455 :                     return LEXRES_OK;
     448          455 :                 }
     449              : 
     450              : }
     451              : 
     452       106023 : <<EOF>>         {
     453              :                     if (cur_state->buffer_stack == NULL)
     454       106023 :                     {
     455              :                         cur_state->start_state = YY_START;
     456       106023 :                         return LEXRES_EOL;      /* end of input reached */
     457       106023 :                     }
     458              : 
     459              :                     /*
     460              :                      * We were expanding a variable, so pop the inclusion
     461              :                      * stack and keep lexing
     462              :                      */
     463              :                     psqlscan_pop_buffer_stack(cur_state);
     464            0 :                     psqlscan_select_top_buffer(cur_state);
     465            0 :                 }
     466              : 
     467            0 : %%
     468            0 : 
     469              : /* LCOV_EXCL_STOP */
     470              : 
     471              : /*
     472              :  * Scan the command name of a psql backslash command.  This should be called
     473              :  * after psql_scan() returns PSCAN_BACKSLASH.  It is assumed that the input
     474              :  * has been consumed through the leading backslash.
     475              :  *
     476              :  * The return value is a malloc'd copy of the command name, as parsed off
     477              :  * from the input.
     478              :  */
     479              : char *
     480              : psql_scan_slash_command(PsqlScanState state)
     481        28318 : {
     482              :     PQExpBufferData mybuf;
     483              : 
     484              :     /* Must be scanning already */
     485              :     Assert(state->scanbufhandle != NULL);
     486              : 
     487              :     /* Build a local buffer that we'll return the data of */
     488              :     initPQExpBuffer(&mybuf);
     489        28318 : 
     490              :     /* Set current output target */
     491              :     state->output_buf = &mybuf;
     492        28318 : 
     493              :     /* Set input source */
     494              :     if (state->buffer_stack != NULL)
     495        28318 :         yy_switch_to_buffer(state->buffer_stack->buf, state->scanner);
     496            0 :     else
     497              :         yy_switch_to_buffer(state->scanbufhandle, state->scanner);
     498        28318 : 
     499              :     /*
     500              :      * Set lexer start state.  Note that this is sufficient to switch
     501              :      * state->scanner over to using the tables in this lexer file.
     502              :      */
     503              :     state->start_state = xslashcmd;
     504        28318 : 
     505              :     /* And lex. */
     506              :     yylex(NULL, state->scanner);
     507        28318 : 
     508              :     /* There are no possible errors in this lex state... */
     509              : 
     510              :     /*
     511              :      * In case the caller returns to using the regular SQL lexer, reselect the
     512              :      * appropriate initial state.
     513              :      */
     514              :     psql_scan_reselect_sql_lexer(state);
     515        28318 : 
     516              :     return mybuf.data;
     517        28318 : }
     518              : 
     519              : /*
     520              :  * Parse off the next argument for a backslash command, and return it as a
     521              :  * malloc'd string.  If there are no more arguments, returns NULL.
     522              :  *
     523              :  * type tells what processing, if any, to perform on the option string;
     524              :  * for example, if it's a SQL identifier, we want to downcase any unquoted
     525              :  * letters.
     526              :  *
     527              :  * if quote is not NULL, *quote is set to 0 if no quoting was found, else
     528              :  * the last quote symbol used in the argument.
     529              :  *
     530              :  * if semicolon is true, unquoted trailing semicolon(s) that would otherwise
     531              :  * be taken as part of the option string will be stripped.
     532              :  *
     533              :  * NOTE: the only possible syntax errors for backslash options are unmatched
     534              :  * quotes, which are detected when we run out of input.  Therefore, on a
     535              :  * syntax error we just throw away the string and return NULL; there is no
     536              :  * need to worry about flushing remaining input.
     537              :  */
     538              : char *
     539              : psql_scan_slash_option(PsqlScanState state,
     540        79714 :                        enum slash_option_type type,
     541              :                        char *quote,
     542              :                        bool semicolon)
     543              : {
     544              :     PQExpBufferData mybuf;
     545              :     int         lexresult PG_USED_FOR_ASSERTS_ONLY;
     546              :     int         final_state;
     547              :     char        local_quote;
     548              : 
     549              :     /* Must be scanning already */
     550              :     Assert(state->scanbufhandle != NULL);
     551              : 
     552              :     if (quote == NULL)
     553        79714 :         quote = &local_quote;
     554        39090 :     *quote = 0;
     555        79714 : 
     556              :     /* Build a local buffer that we'll return the data of */
     557              :     initPQExpBuffer(&mybuf);
     558        79714 : 
     559              :     /* Set up static variables that will be used by yylex */
     560              :     option_type = type;
     561        79714 :     option_quote = quote;
     562        79714 :     unquoted_option_chars = 0;
     563        79714 : 
     564              :     /* Set current output target */
     565              :     state->output_buf = &mybuf;
     566        79714 : 
     567              :     /* Set input source */
     568              :     if (state->buffer_stack != NULL)
     569        79714 :         yy_switch_to_buffer(state->buffer_stack->buf, state->scanner);
     570            0 :     else
     571              :         yy_switch_to_buffer(state->scanbufhandle, state->scanner);
     572        79714 : 
     573              :     /* Set lexer start state */
     574              :     if (type == OT_WHOLE_LINE)
     575        79714 :         state->start_state = xslashwholeline;
     576          809 :     else
     577              :         state->start_state = xslashargstart;
     578        78905 : 
     579              :     /* And lex. */
     580              :     lexresult = yylex(NULL, state->scanner);
     581        79714 : 
     582              :     /* Save final state for a moment... */
     583              :     final_state = state->start_state;
     584        79714 : 
     585              :     /*
     586              :      * In case the caller returns to using the regular SQL lexer, reselect the
     587              :      * appropriate initial state.
     588              :      */
     589              :     psql_scan_reselect_sql_lexer(state);
     590        79714 : 
     591              :     /*
     592              :      * Check the lex result: we should have gotten back either LEXRES_OK or
     593              :      * LEXRES_EOL (the latter indicating end of string).  If we were inside a
     594              :      * quoted string, as indicated by final_state, EOL is an error.
     595              :      */
     596              :     Assert(lexresult == LEXRES_EOL || lexresult == LEXRES_OK);
     597              : 
     598              :     switch (final_state)
     599        79714 :     {
     600              :         case xslashargstart:
     601        49766 :             /* empty arg */
     602              :             break;
     603        49766 :         case xslasharg:
     604        29132 :             /* Strip any unquoted trailing semicolons if requested */
     605              :             if (semicolon)
     606        29132 :             {
     607              :                 while (unquoted_option_chars-- > 0 &&
     608         3721 :                        mybuf.len > 0 &&
     609         3724 :                        mybuf.data[mybuf.len - 1] == ';')
     610         3293 :                 {
     611              :                     mybuf.data[--mybuf.len] = '\0';
     612            3 :                 }
     613              :             }
     614              : 
     615              :             /*
     616              :              * If SQL identifier processing was requested, then we strip out
     617              :              * excess double quotes and optionally downcase unquoted letters.
     618              :              */
     619              :             if (type == OT_SQLID || type == OT_SQLIDHACK)
     620        29132 :             {
     621              :                 dequote_downcase_identifier(mybuf.data,
     622          148 :                                             (type != OT_SQLIDHACK),
     623              :                                             state->encoding);
     624              :                 /* update mybuf.len for possible shortening */
     625              :                 mybuf.len = strlen(mybuf.data);
     626          148 :             }
     627              :             break;
     628        29132 :         case xslashquote:
     629            0 :         case xslashbackquote:
     630              :         case xslashdquote:
     631              :             /* must have hit EOL inside quotes */
     632              :             pg_log_error("unterminated quoted string");
     633            0 :             termPQExpBuffer(&mybuf);
     634            0 :             return NULL;
     635            0 :         case xslashwholeline:
     636          816 : 
     637              :             /*
     638              :              * In whole-line mode, we interpret semicolon = true as stripping
     639              :              * trailing whitespace as well as semicolons; this gives the
     640              :              * nearest equivalent to what semicolon = true does in normal
     641              :              * mode.  Note there's no concept of quoting in this mode.
     642              :              */
     643              :             if (semicolon)
     644          816 :             {
     645              :                 while (mybuf.len > 0 &&
     646          110 :                        (mybuf.data[mybuf.len - 1] == ';' ||
     647          109 :                         (isascii((unsigned char) mybuf.data[mybuf.len - 1]) &&
     648           94 :                          isspace((unsigned char) mybuf.data[mybuf.len - 1]))))
     649           94 :                 {
     650              :                     mybuf.data[--mybuf.len] = '\0';
     651           15 :                 }
     652              :             }
     653              :             break;
     654          816 :         default:
     655            0 :             /* can't get here */
     656              :             fprintf(stderr, "invalid YY_START\n");
     657            0 :             exit(1);
     658            0 :     }
     659              : 
     660              :     /*
     661              :      * An unquoted empty argument isn't possible unless we are at end of
     662              :      * command.  Return NULL instead.
     663              :      */
     664              :     if (mybuf.len == 0 && *quote == 0)
     665        79714 :     {
     666              :         termPQExpBuffer(&mybuf);
     667        51295 :         return NULL;
     668        51295 :     }
     669              : 
     670              :     /* Else return the completed string. */
     671              :     return mybuf.data;
     672        28419 : }
     673              : 
     674              : /*
     675              :  * Eat up any unused \\ to complete a backslash command.
     676              :  */
     677              : void
     678              : psql_scan_slash_command_end(PsqlScanState state)
     679        28316 : {
     680              :     /* Must be scanning already */
     681              :     Assert(state->scanbufhandle != NULL);
     682              : 
     683              :     /* Set current output target */
     684              :     state->output_buf = NULL;    /* we won't output anything */
     685        28316 : 
     686              :     /* Set input source */
     687              :     if (state->buffer_stack != NULL)
     688        28316 :         yy_switch_to_buffer(state->buffer_stack->buf, state->scanner);
     689            0 :     else
     690              :         yy_switch_to_buffer(state->scanbufhandle, state->scanner);
     691        28316 : 
     692              :     /* Set lexer start state */
     693              :     state->start_state = xslashend;
     694        28316 : 
     695              :     /* And lex. */
     696              :     yylex(NULL, state->scanner);
     697        28316 : 
     698              :     /* There are no possible errors in this lex state... */
     699              : 
     700              :     /*
     701              :      * We expect the caller to return to using the regular SQL lexer, so
     702              :      * reselect the appropriate initial state.
     703              :      */
     704              :     psql_scan_reselect_sql_lexer(state);
     705        28316 : }
     706        28316 : 
     707              : /*
     708              :  * Fetch current paren nesting depth
     709              :  */
     710              : int
     711              : psql_scan_get_paren_depth(PsqlScanState state)
     712          143 : {
     713              :     return state->paren_depth;
     714          143 : }
     715              : 
     716              : /*
     717              :  * Set paren nesting depth
     718              :  */
     719              : void
     720              : psql_scan_set_paren_depth(PsqlScanState state, int depth)
     721          121 : {
     722              :     Assert(depth >= 0);
     723              :     state->paren_depth = depth;
     724          121 : }
     725          121 : 
     726              : /*
     727              :  * De-quote and optionally downcase a SQL identifier.
     728              :  *
     729              :  * The string at *str is modified in-place; it can become shorter,
     730              :  * but not longer.
     731              :  *
     732              :  * If downcase is true then non-quoted letters are folded to lower case.
     733              :  * Ideally this behavior will match the backend's downcase_identifier();
     734              :  * but note that it could differ if LC_CTYPE is different in the frontend.
     735              :  *
     736              :  * Note that a string like FOO"BAR"BAZ will be converted to fooBARbaz;
     737              :  * this is somewhat inconsistent with the SQL spec, which would have us
     738              :  * parse it as several identifiers.  But for psql's purposes, we want a
     739              :  * string like "foo"."bar" to be treated as one option, so there's little
     740              :  * choice; this routine doesn't get to change the token boundaries.
     741              :  */
     742              : void
     743              : dequote_downcase_identifier(char *str, bool downcase, int encoding)
     744          244 : {
     745              :     bool        inquotes = false;
     746          244 :     char       *cp = str;
     747          244 : 
     748              :     while (*cp)
     749         1844 :     {
     750              :         if (*cp == '"')
     751         1600 :         {
     752              :             if (inquotes && cp[1] == '"')
     753           68 :             {
     754              :                 /* Keep the first quote, remove the second */
     755              :                 cp++;
     756           10 :             }
     757              :             else
     758              :                 inquotes = !inquotes;
     759           58 :             /* Collapse out quote at *cp */
     760              :             memmove(cp, cp + 1, strlen(cp));
     761           68 :             /* do not advance cp */
     762              :         }
     763              :         else
     764              :         {
     765              :             if (downcase && !inquotes)
     766         1532 :                 *cp = pg_tolower((unsigned char) *cp);
     767          138 :             cp += PQmblenBounded(cp, encoding);
     768         1532 :         }
     769              :     }
     770              : }
     771          244 : 
     772              : /*
     773              :  * Evaluate a backticked substring of a slash command's argument.
     774              :  *
     775              :  * The portion of output_buf starting at backtick_start_offset is evaluated
     776              :  * as a shell command and then replaced by the command's output.
     777              :  */
     778              : static void
     779              : evaluate_backtick(PsqlScanState state)
     780            0 : {
     781              :     PQExpBuffer output_buf = state->output_buf;
     782            0 :     char       *cmd = output_buf->data + backtick_start_offset;
     783            0 :     PQExpBufferData cmd_output;
     784              :     FILE       *fd;
     785              :     bool        error = false;
     786            0 :     int         exit_code = 0;
     787            0 :     char        buf[512];
     788              :     size_t      result;
     789              : 
     790              :     initPQExpBuffer(&cmd_output);
     791            0 : 
     792              :     fflush(NULL);
     793            0 :     fd = popen(cmd, "r");
     794            0 :     if (!fd)
     795            0 :     {
     796              :         pg_log_error("%s: %m", cmd);
     797            0 :         error = true;
     798            0 :         exit_code = -1;
     799            0 :     }
     800              : 
     801              :     if (!error)
     802            0 :     {
     803              :         do
     804              :         {
     805              :             result = fread(buf, 1, sizeof(buf), fd);
     806            0 :             if (ferror(fd))
     807            0 :             {
     808              :                 pg_log_error("%s: %m", cmd);
     809            0 :                 error = true;
     810            0 :                 break;
     811            0 :             }
     812              :             appendBinaryPQExpBuffer(&cmd_output, buf, result);
     813            0 :         } while (!feof(fd));
     814            0 :     }
     815              : 
     816              :     if (fd)
     817            0 :     {
     818              :         /*
     819              :          * Although pclose's result always sets the shell result variables, we
     820              :          * historically have abandoned the backtick substitution only if it
     821              :          * returns -1.
     822              :          */
     823              :         exit_code = pclose(fd);
     824            0 :         if (exit_code == -1)
     825            0 :         {
     826              :             pg_log_error("%s: %m", cmd);
     827            0 :             error = true;
     828            0 :         }
     829              :     }
     830              : 
     831              :     if (PQExpBufferDataBroken(cmd_output))
     832            0 :     {
     833              :         pg_log_error("%s: out of memory", cmd);
     834            0 :         error = true;
     835            0 :     }
     836              : 
     837              :     /* Now done with cmd, delete it from output_buf */
     838              :     output_buf->len = backtick_start_offset;
     839            0 :     output_buf->data[output_buf->len] = '\0';
     840            0 : 
     841              :     /* If no error, transfer result to output_buf */
     842              :     if (!error)
     843            0 :     {
     844              :         /* strip any trailing newline (but only one) */
     845              :         if (cmd_output.len > 0 &&
     846            0 :             cmd_output.data[cmd_output.len - 1] == '\n')
     847            0 :             cmd_output.len--;
     848            0 :         appendBinaryPQExpBuffer(output_buf, cmd_output.data, cmd_output.len);
     849            0 :     }
     850              : 
     851              :     /* And finally, set the shell result variables */
     852              :     SetShellResultVariables(exit_code);
     853            0 : 
     854              :     termPQExpBuffer(&cmd_output);
     855            0 : }
     856            0 : /* END: function "evaluate_backtick" */
        

Generated by: LCOV version 2.0-1