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

Generated by: LCOV version 1.14