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

Generated by: LCOV version 1.11