LCOV - code coverage report
Current view: top level - src/bin/psql - psqlscanslash.l (source / functions) Hit Total Coverage
Test: PostgreSQL 18devel Lines: 77 125 61.6 %
Date: 2025-01-18 04:15:08 Functions: 6 7 85.7 %
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-2025, 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             :         PQExpBuffer output_buf = cur_state->output_buf;
     120             : 
     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             : %}
     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             : 
     138             :     /*
     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             : 
     145             : {space}|"\\"  {
     146             :                     yyless(0);
     147             :                     cur_state->start_state = YY_START;
     148             :                     return LEXRES_OK;
     149             :                 }
     150             : 
     151             : {other}         { ECHO; }
     152             : 
     153             : }
     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             : 
     162             : {space}+        { }
     163             : 
     164             : "|"               {
     165             :                     if (option_type == OT_FILEPIPE)
     166             :                     {
     167             :                         /* treat like whole-string case */
     168             :                         ECHO;
     169             :                         BEGIN(xslashwholeline);
     170             :                     }
     171             :                     else
     172             :                     {
     173             :                         /* vertical bar is not special otherwise */
     174             :                         yyless(0);
     175             :                         BEGIN(xslasharg);
     176             :                     }
     177             :                 }
     178             : 
     179             : {other}         {
     180             :                     yyless(0);
     181             :                     BEGIN(xslasharg);
     182             :                 }
     183             : 
     184             : }
     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             : 
     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             :                     cur_state->start_state = YY_START;
     207             :                     return LEXRES_OK;
     208             :                 }
     209             : 
     210             : {quote}         {
     211             :                     *option_quote = '\'';
     212             :                     unquoted_option_chars = 0;
     213             :                     BEGIN(xslashquote);
     214             :                 }
     215             : 
     216             : "`"               {
     217             :                     backtick_start_offset = output_buf->len;
     218             :                     *option_quote = '`';
     219             :                     unquoted_option_chars = 0;
     220             :                     BEGIN(xslashbackquote);
     221             :                 }
     222             : 
     223             : {dquote}        {
     224             :                     ECHO;
     225             :                     *option_quote = '"';
     226             :                     unquoted_option_chars = 0;
     227             :                     BEGIN(xslashdquote);
     228             :                 }
     229             : 
     230             : :{variable_char}+   {
     231             :                     /* Possible psql variable substitution */
     232             :                     if (cur_state->callbacks->get_variable == NULL)
     233             :                         ECHO;
     234             :                     else
     235             :                     {
     236             :                         char       *varname;
     237             :                         char       *value;
     238             : 
     239             :                         varname = psqlscan_extract_substring(cur_state,
     240             :                                                              yytext + 1,
     241             :                                                              yyleng - 1);
     242             :                         value = cur_state->callbacks->get_variable(varname,
     243             :                                                                    PQUOTE_PLAIN,
     244             :                                                                    cur_state->cb_passthrough);
     245             :                         free(varname);
     246             : 
     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             :                         {
     256             :                             appendPQExpBufferStr(output_buf, value);
     257             :                             free(value);
     258             :                         }
     259             :                         else
     260             :                             ECHO;
     261             : 
     262             :                         *option_quote = ':';
     263             :                     }
     264             :                     unquoted_option_chars = 0;
     265             :                 }
     266             : 
     267             : :'{variable_char}+' {
     268             :                     psqlscan_escape_variable(cur_state, yytext, yyleng,
     269             :                                              PQUOTE_SQL_LITERAL);
     270             :                     *option_quote = ':';
     271             :                     unquoted_option_chars = 0;
     272             :                 }
     273             : 
     274             : 
     275             : :\"{variable_char}+\" {
     276             :                     psqlscan_escape_variable(cur_state, yytext, yyleng,
     277             :                                              PQUOTE_SQL_IDENT);
     278             :                     *option_quote = ':';
     279             :                     unquoted_option_chars = 0;
     280             :                 }
     281             : 
     282             : :\{\?{variable_char}+\} {
     283             :                     psqlscan_test_variable(cur_state, yytext, yyleng);
     284             :                 }
     285             : 
     286             : :'{variable_char}*  {
     287             :                     /* Throw back everything but the colon */
     288             :                     yyless(1);
     289             :                     unquoted_option_chars++;
     290             :                     ECHO;
     291             :                 }
     292             : 
     293             : :\"{variable_char}*    {
     294             :                     /* Throw back everything but the colon */
     295             :                     yyless(1);
     296             :                     unquoted_option_chars++;
     297             :                     ECHO;
     298             :                 }
     299             : 
     300             : :\{\?{variable_char}*   {
     301             :                     /* Throw back everything but the colon */
     302             :                     yyless(1);
     303             :                     unquoted_option_chars++;
     304             :                     ECHO;
     305             :                 }
     306             : 
     307             : :\{     {
     308             :                     /* Throw back everything but the colon */
     309             :                     yyless(1);
     310             :                     unquoted_option_chars++;
     311             :                     ECHO;
     312             :                 }
     313             : 
     314             : {other}         {
     315             :                     unquoted_option_chars++;
     316             :                     ECHO;
     317             :                 }
     318             : 
     319             : }
     320             : 
     321             : <xslashquote>{
     322             :     /*
     323             :      * single-quoted text: copy literally except for '' and backslash
     324             :      * sequences
     325             :      */
     326             : 
     327             : {quote}         { BEGIN(xslasharg); }
     328             : 
     329             : {xqdouble}      { appendPQExpBufferChar(output_buf, '\''); }
     330             : 
     331             : "\\n"         { appendPQExpBufferChar(output_buf, '\n'); }
     332             : "\\t"         { appendPQExpBufferChar(output_buf, '\t'); }
     333             : "\\b"         { appendPQExpBufferChar(output_buf, '\b'); }
     334             : "\\r"         { appendPQExpBufferChar(output_buf, '\r'); }
     335             : "\\f"         { appendPQExpBufferChar(output_buf, '\f'); }
     336             : 
     337             : {xeoctesc}      {
     338             :                     /* octal case */
     339             :                     appendPQExpBufferChar(output_buf,
     340             :                                           (char) strtol(yytext + 1, NULL, 8));
     341             :                 }
     342             : 
     343             : {xehexesc}      {
     344             :                     /* hex case */
     345             :                     appendPQExpBufferChar(output_buf,
     346             :                                           (char) strtol(yytext + 2, NULL, 16));
     347             :                 }
     348             : 
     349             : "\\".         { psqlscan_emit(cur_state, yytext + 1, 1); }
     350             : 
     351             : {other}|\n      { ECHO; }
     352             : 
     353             : }
     354             : 
     355             : <xslashbackquote>{
     356             :     /*
     357             :      * backticked text: copy everything until next backquote (expanding
     358             :      * variable references, but doing nought else), then evaluate.
     359             :      */
     360             : 
     361             : "`"               {
     362             :                     /* In an inactive \if branch, don't evaluate the command */
     363             :                     if (cur_state->cb_passthrough == NULL ||
     364             :                         conditional_active((ConditionalStack) cur_state->cb_passthrough))
     365             :                         evaluate_backtick(cur_state);
     366             :                     BEGIN(xslasharg);
     367             :                 }
     368             : 
     369             : :{variable_char}+   {
     370             :                     /* Possible psql variable substitution */
     371             :                     if (cur_state->callbacks->get_variable == NULL)
     372             :                         ECHO;
     373             :                     else
     374             :                     {
     375             :                         char       *varname;
     376             :                         char       *value;
     377             : 
     378             :                         varname = psqlscan_extract_substring(cur_state,
     379             :                                                              yytext + 1,
     380             :                                                              yyleng - 1);
     381             :                         value = cur_state->callbacks->get_variable(varname,
     382             :                                                                    PQUOTE_PLAIN,
     383             :                                                                    cur_state->cb_passthrough);
     384             :                         free(varname);
     385             : 
     386             :                         if (value)
     387             :                         {
     388             :                             appendPQExpBufferStr(output_buf, value);
     389             :                             free(value);
     390             :                         }
     391             :                         else
     392             :                             ECHO;
     393             :                     }
     394             :                 }
     395             : 
     396             : :'{variable_char}+' {
     397             :                     psqlscan_escape_variable(cur_state, yytext, yyleng,
     398             :                                              PQUOTE_SHELL_ARG);
     399             :                 }
     400             : 
     401             : :'{variable_char}*  {
     402             :                     /* Throw back everything but the colon */
     403             :                     yyless(1);
     404             :                     ECHO;
     405             :                 }
     406             : 
     407             : {other}|\n      { ECHO; }
     408             : 
     409             : }
     410             : 
     411             : <xslashdquote>{
     412             :     /* double-quoted text: copy verbatim, including the double quotes */
     413             : 
     414             : {dquote}        {
     415             :                     ECHO;
     416             :                     BEGIN(xslasharg);
     417             :                 }
     418             : 
     419             : {other}|\n      { ECHO; }
     420             : 
     421             : }
     422             : 
     423             : <xslashwholeline>{
     424             :     /* copy everything until end of input line */
     425             :     /* but suppress leading whitespace */
     426             : 
     427             : {space}+        {
     428             :                     if (output_buf->len > 0)
     429             :                         ECHO;
     430             :                 }
     431             : 
     432             : {other}         { ECHO; }
     433             : 
     434             : }
     435             : 
     436             : <xslashend>{
     437             :     /* at end of command, eat a double backslash, but not anything else */
     438             : 
     439             : "\\\\"            {
     440             :                     cur_state->start_state = YY_START;
     441             :                     return LEXRES_OK;
     442             :                 }
     443             : 
     444             : {other}|\n      {
     445             :                     yyless(0);
     446             :                     cur_state->start_state = YY_START;
     447             :                     return LEXRES_OK;
     448             :                 }
     449             : 
     450             : }
     451             : 
     452             : <<EOF>>         {
     453             :                     if (cur_state->buffer_stack == NULL)
     454             :                     {
     455             :                         cur_state->start_state = YY_START;
     456             :                         return LEXRES_EOL;      /* end of input reached */
     457             :                     }
     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             :                     psqlscan_select_top_buffer(cur_state);
     465             :                 }
     466             : 
     467             : %%
     468             : 
     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       14018 : {
     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       14018 : 
     490             :     /* Set current output target */
     491             :     state->output_buf = &mybuf;
     492       14018 : 
     493             :     /* Set input source */
     494             :     if (state->buffer_stack != NULL)
     495       14018 :         yy_switch_to_buffer(state->buffer_stack->buf, state->scanner);
     496           0 :     else
     497             :         yy_switch_to_buffer(state->scanbufhandle, state->scanner);
     498       14018 : 
     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       14018 : 
     505             :     /* And lex. */
     506             :     yylex(NULL, state->scanner);
     507       14018 : 
     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       14018 : 
     516             :     return mybuf.data;
     517       14018 : }
     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       35982 :                        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       35982 :         quote = &local_quote;
     554       32574 :     *quote = 0;
     555       35982 : 
     556             :     /* Build a local buffer that we'll return the data of */
     557             :     initPQExpBuffer(&mybuf);
     558       35982 : 
     559             :     /* Set up static variables that will be used by yylex */
     560             :     option_type = type;
     561       35982 :     option_quote = quote;
     562       35982 :     unquoted_option_chars = 0;
     563       35982 : 
     564             :     /* Set current output target */
     565             :     state->output_buf = &mybuf;
     566       35982 : 
     567             :     /* Set input source */
     568             :     if (state->buffer_stack != NULL)
     569       35982 :         yy_switch_to_buffer(state->buffer_stack->buf, state->scanner);
     570           0 :     else
     571             :         yy_switch_to_buffer(state->scanbufhandle, state->scanner);
     572       35982 : 
     573             :     /* Set lexer start state */
     574             :     if (type == OT_WHOLE_LINE)
     575       35982 :         state->start_state = xslashwholeline;
     576        1484 :     else
     577             :         state->start_state = xslashargstart;
     578       34498 : 
     579             :     /* And lex. */
     580             :     lexresult = yylex(NULL, state->scanner);
     581       35982 : 
     582             :     /* Save final state for a moment... */
     583             :     final_state = state->start_state;
     584       35982 : 
     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       35982 : 
     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       35982 :     {
     600             :         case xslashargstart:
     601       18148 :             /* empty arg */
     602             :             break;
     603       18148 :         case xslasharg:
     604       16336 :             /* Strip any unquoted trailing semicolons if requested */
     605             :             if (semicolon)
     606       16336 :             {
     607             :                 while (unquoted_option_chars-- > 0 &&
     608        6910 :                        mybuf.len > 0 &&
     609        6048 :                        mybuf.data[mybuf.len - 1] == ';')
     610        6048 :                 {
     611             :                     mybuf.data[--mybuf.len] = '\0';
     612           6 :                 }
     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       16336 :             {
     621             :                 dequote_downcase_identifier(mybuf.data,
     622         266 :                                             (type != OT_SQLIDHACK),
     623             :                                             state->encoding);
     624             :                 /* update mybuf.len for possible shortening */
     625             :                 mybuf.len = strlen(mybuf.data);
     626         266 :             }
     627             :             break;
     628       16336 :         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        1498 : 
     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        1498 :             {
     645             :                 while (mybuf.len > 0 &&
     646         220 :                        (mybuf.data[mybuf.len - 1] == ';' ||
     647         218 :                         (isascii((unsigned char) mybuf.data[mybuf.len - 1]) &&
     648         188 :                          isspace((unsigned char) mybuf.data[mybuf.len - 1]))))
     649         188 :                 {
     650             :                     mybuf.data[--mybuf.len] = '\0';
     651          30 :                 }
     652             :             }
     653             :             break;
     654        1498 :         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       35982 :     {
     666             :         termPQExpBuffer(&mybuf);
     667       19780 :         return NULL;
     668       19780 :     }
     669             : 
     670             :     /* Else return the completed string. */
     671             :     return mybuf.data;
     672       16202 : }
     673             : 
     674             : /*
     675             :  * Eat up any unused \\ to complete a backslash command.
     676             :  */
     677             : void
     678             : psql_scan_slash_command_end(PsqlScanState state)
     679       14016 : {
     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       14016 : 
     686             :     /* Set input source */
     687             :     if (state->buffer_stack != NULL)
     688       14016 :         yy_switch_to_buffer(state->buffer_stack->buf, state->scanner);
     689           0 :     else
     690             :         yy_switch_to_buffer(state->scanbufhandle, state->scanner);
     691       14016 : 
     692             :     /* Set lexer start state */
     693             :     state->start_state = xslashend;
     694       14016 : 
     695             :     /* And lex. */
     696             :     yylex(NULL, state->scanner);
     697       14016 : 
     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       14016 : }
     706       14016 : 
     707             : /*
     708             :  * Fetch current paren nesting depth
     709             :  */
     710             : int
     711             : psql_scan_get_paren_depth(PsqlScanState state)
     712         252 : {
     713             :     return state->paren_depth;
     714         252 : }
     715             : 
     716             : /*
     717             :  * Set paren nesting depth
     718             :  */
     719             : void
     720             : psql_scan_set_paren_depth(PsqlScanState state, int depth)
     721         216 : {
     722             :     Assert(depth >= 0);
     723             :     state->paren_depth = depth;
     724         216 : }
     725         216 : 
     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         458 : {
     745             :     bool        inquotes = false;
     746         458 :     char       *cp = str;
     747         458 : 
     748             :     while (*cp)
     749        3572 :     {
     750             :         if (*cp == '"')
     751        3114 :         {
     752             :             if (inquotes && cp[1] == '"')
     753         136 :             {
     754             :                 /* Keep the first quote, remove the second */
     755             :                 cp++;
     756          20 :             }
     757             :             else
     758             :                 inquotes = !inquotes;
     759         116 :             /* Collapse out quote at *cp */
     760             :             memmove(cp, cp + 1, strlen(cp));
     761         136 :             /* do not advance cp */
     762             :         }
     763             :         else
     764             :         {
     765             :             if (downcase && !inquotes)
     766        2978 :                 *cp = pg_tolower((unsigned char) *cp);
     767         276 :             cp += PQmblenBounded(cp, encoding);
     768        2978 :         }
     769             :     }
     770             : }
     771         458 : 
     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 : }

Generated by: LCOV version 1.14