LCOV - code coverage report
Current view: top level - src/bin/psql - psqlscanslash.l (source / functions) Coverage Total Hit
Test: Code coverage Lines: 61.7 % 149 92
Test Date: 2026-01-26 10:56:24 Functions: 85.7 % 7 6
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 60.0 % 85 51

             Branch data     Line data    Source code
       1                 :             : %top{
       2                 :             : /*-------------------------------------------------------------------------
       3                 :             :  *
       4                 :             :  * psqlscanslash.l
       5                 :             :  *        lexical scanner for psql backslash commands
       6                 :             :  *
       7                 :             :  * XXX Avoid creating backtracking cases --- see the backend lexer for info.
       8                 :             :  *
       9                 :             :  * See fe_utils/psqlscan_int.h for additional commentary.
      10                 :             :  *
      11                 :             :  * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
      12                 :             :  * Portions Copyright (c) 1994, Regents of the University of California
      13                 :             :  *
      14                 :             :  * IDENTIFICATION
      15                 :             :  *        src/bin/psql/psqlscanslash.l
      16                 :             :  *
      17                 :             :  *-------------------------------------------------------------------------
      18                 :             :  */
      19                 :             : #include "postgres_fe.h"
      20                 :             : 
      21                 :             : #include <ctype.h>
      22                 :             : 
      23                 :             : #include "common.h"
      24                 :             : #include "psqlscanslash.h"
      25                 :             : 
      26                 :             : #include "common/logging.h"
      27                 :             : #include "fe_utils/conditional.h"
      28                 :             : 
      29                 :             : #include "libpq-fe.h"
      30                 :             : }
      31                 :             : 
      32                 :             : %{
      33                 :             : #include "fe_utils/psqlscan_int.h"
      34                 :             : 
      35                 :             : /*
      36                 :             :  * We must have a typedef YYSTYPE for yylex's first argument, but this lexer
      37                 :             :  * doesn't presently make use of that argument, so just declare it as int.
      38                 :             :  */
      39                 :             : typedef int YYSTYPE;
      40                 :             : 
      41                 :             : /*
      42                 :             :  * These variables do not need to be saved across calls.  Yeah, it's a bit
      43                 :             :  * of a hack, but putting them into PsqlScanStateData would be klugy too.
      44                 :             :  */
      45                 :             : static enum slash_option_type option_type;
      46                 :             : static char *option_quote;
      47                 :             : static int      unquoted_option_chars;
      48                 :             : static int      backtick_start_offset;
      49                 :             : 
      50                 :             : 
      51                 :             : /* Return values from yylex() */
      52                 :             : #define LEXRES_EOL                      0       /* end of input */
      53                 :             : #define LEXRES_OK                       1       /* OK completion of backslash argument */
      54                 :             : 
      55                 :             : 
      56                 :             : static void evaluate_backtick(PsqlScanState state);
      57                 :             : 
      58                 :             : #define ECHO psqlscan_emit(cur_state, yytext, yyleng)
      59                 :             : 
      60                 :             : /* LCOV_EXCL_START */
      61                 :             : 
      62                 :             : %}
      63                 :             : 
      64                 :             : /* Except for the prefix, these options should match psqlscan.l */
      65                 :             : %option reentrant
      66                 :             : %option bison-bridge
      67                 :             : %option 8bit
      68                 :             : %option never-interactive
      69                 :             : %option nodefault
      70                 :             : %option noinput
      71                 :             : %option nounput
      72                 :             : %option noyywrap
      73                 :             : %option warn
      74                 :             : %option prefix="slash_yy"
      75                 :             : 
      76                 :             : /*
      77                 :             :  * Set the type of yyextra; we use it as a pointer back to the containing
      78                 :             :  * PsqlScanState.
      79                 :             :  */
      80                 :             : %option extra-type="PsqlScanState"
      81                 :             : 
      82                 :             : /*
      83                 :             :  * OK, here is a short description of lex/flex rules behavior.
      84                 :             :  * The longest pattern which matches an input string is always chosen.
      85                 :             :  * For equal-length patterns, the first occurring in the rules list is chosen.
      86                 :             :  * INITIAL is the starting state, to which all non-conditional rules apply.
      87                 :             :  * Exclusive states change parsing rules while the state is active.  When in
      88                 :             :  * an exclusive state, only those rules defined for that state apply.
      89                 :             :  */
      90                 :             : 
      91                 :             : /* Exclusive states for lexing backslash commands */
      92                 :             : %x xslashcmd
      93                 :             : %x xslashargstart
      94                 :             : %x xslasharg
      95                 :             : %x xslashquote
      96                 :             : %x xslashbackquote
      97                 :             : %x xslashdquote
      98                 :             : %x xslashwholeline
      99                 :             : %x xslashend
     100                 :             : 
     101                 :             : /*
     102                 :             :  * Assorted character class definitions that should match psqlscan.l.
     103                 :             :  */
     104                 :             : space                   [ \t\n\r\f\v]
     105                 :             : quote                   '
     106                 :             : xeoctesc                [\\][0-7]{1,3}
     107                 :             : xehexesc                [\\]x[0-9A-Fa-f]{1,2}
     108                 :             : xqdouble                {quote}{quote}
     109                 :             : dquote                  \"
     110                 :             : variable_char   [A-Za-z\200-\377_0-9]
     111                 :             : 
     112                 :             : other                   .
     113                 :             : 
     114                 :             : %%
     115                 :             : 
     116                 :             : %{
     117                 :             :                 /* Declare some local variables inside yylex(), for convenience */
     118                 :             :                 PsqlScanState cur_state = yyextra;
     119                 :             :                 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                 :        2664 : {
     482                 :             :         PQExpBufferData mybuf;
     483                 :        2664 : 
     484                 :             :         /* Must be scanning already */
     485                 :             :         Assert(state->scanbufhandle != NULL);
     486         [ +  - ]:        2664 : 
     487                 :             :         /* Build a local buffer that we'll return the data of */
     488                 :             :         initPQExpBuffer(&mybuf);
     489                 :        2664 : 
     490                 :             :         /* Set current output target */
     491                 :             :         state->output_buf = &mybuf;
     492                 :        2664 : 
     493                 :             :         /* Set input source */
     494                 :             :         if (state->buffer_stack != NULL)
     495         [ -  + ]:        2664 :                 yy_switch_to_buffer(state->buffer_stack->buf, state->scanner);
     496                 :           0 :         else
     497                 :             :                 yy_switch_to_buffer(state->scanbufhandle, state->scanner);
     498                 :        2664 : 
     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                 :        2664 : 
     505                 :             :         /* And lex. */
     506                 :             :         yylex(NULL, state->scanner);
     507                 :        2664 : 
     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                 :        2664 : 
     516                 :             :         return mybuf.data;
     517                 :        5328 : }
     518                 :        2664 : 
     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                 :        6420 :                                            enum slash_option_type type,
     541                 :             :                                            char *quote,
     542                 :             :                                            bool semicolon)
     543                 :             : {
     544                 :             :         PQExpBufferData mybuf;
     545                 :        6420 :         int                     lexresult PG_USED_FOR_ASSERTS_ONLY;
     546                 :        6420 :         int                     final_state;
     547                 :        6420 :         char            local_quote;
     548                 :        6420 : 
     549                 :             :         /* Must be scanning already */
     550                 :             :         Assert(state->scanbufhandle != NULL);
     551         [ +  - ]:        6420 : 
     552                 :             :         if (quote == NULL)
     553         [ +  + ]:        6420 :                 quote = &local_quote;
     554                 :        5961 :         *quote = 0;
     555                 :        6420 : 
     556                 :             :         /* Build a local buffer that we'll return the data of */
     557                 :             :         initPQExpBuffer(&mybuf);
     558                 :        6420 : 
     559                 :             :         /* Set up static variables that will be used by yylex */
     560                 :             :         option_type = type;
     561                 :        6420 :         option_quote = quote;
     562                 :        6420 :         unquoted_option_chars = 0;
     563                 :        6420 : 
     564                 :             :         /* Set current output target */
     565                 :             :         state->output_buf = &mybuf;
     566                 :        6420 : 
     567                 :             :         /* Set input source */
     568                 :             :         if (state->buffer_stack != NULL)
     569         [ +  - ]:        6420 :                 yy_switch_to_buffer(state->buffer_stack->buf, state->scanner);
     570                 :           0 :         else
     571                 :             :                 yy_switch_to_buffer(state->scanbufhandle, state->scanner);
     572                 :        6420 : 
     573                 :             :         /* Set lexer start state */
     574                 :             :         if (type == OT_WHOLE_LINE)
     575         [ +  + ]:        6420 :                 state->start_state = xslashwholeline;
     576                 :         245 :         else
     577                 :             :                 state->start_state = xslashargstart;
     578                 :        6175 : 
     579                 :             :         /* And lex. */
     580                 :             :         lexresult = yylex(NULL, state->scanner);
     581                 :        6420 : 
     582                 :             :         /* Save final state for a moment... */
     583                 :             :         final_state = state->start_state;
     584                 :        6420 : 
     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                 :        6420 : 
     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   [ +  +  +  - ]:        6420 : 
     598                 :             :         switch (final_state)
     599   [ +  -  -  +  :        6420 :         {
                      + ]
     600                 :             :                 case xslashargstart:
     601                 :             :                         /* empty arg */
     602                 :             :                         break;
     603                 :             :                 case xslasharg:
     604                 :             :                         /* Strip any unquoted trailing semicolons if requested */
     605                 :             :                         if (semicolon)
     606         [ +  + ]:        2980 :                         {
     607                 :             :                                 while (unquoted_option_chars-- > 0 &&
     608   [ +  +  +  + ]:        2199 :                                            mybuf.len > 0 &&
     609         [ -  + ]:        1030 :                                            mybuf.data[mybuf.len - 1] == ';')
     610                 :        1030 :                                 {
     611                 :             :                                         mybuf.data[--mybuf.len] = '\0';
     612                 :           1 :                                 }
     613                 :             :                         }
     614                 :        1168 : 
     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   [ +  -  +  + ]:        2980 :                         {
     621                 :             :                                 dequote_downcase_identifier(mybuf.data,
     622                 :          56 :                                                                                         (type != OT_SQLIDHACK),
     623                 :          28 :                                                                                         state->encoding);
     624                 :          28 :                                 /* update mybuf.len for possible shortening */
     625                 :             :                                 mybuf.len = strlen(mybuf.data);
     626                 :          28 :                         }
     627                 :          28 :                         break;
     628                 :        2980 :                 case xslashquote:
     629                 :             :                 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                 :             : 
     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         [ +  + ]:         246 :                         {
     645                 :             :                                 while (mybuf.len > 0 &&
     646   [ -  +  +  + ]:          63 :                                            (mybuf.data[mybuf.len - 1] == ';' ||
     647         [ +  + ]:          63 :                                                 (isascii((unsigned char) mybuf.data[mybuf.len - 1]) &&
     648         [ -  + ]:          29 :                                                  isspace((unsigned char) mybuf.data[mybuf.len - 1]))))
     649                 :          29 :                                 {
     650                 :             :                                         mybuf.data[--mybuf.len] = '\0';
     651                 :           5 :                                 }
     652                 :             :                         }
     653                 :          29 :                         break;
     654                 :         246 :                 default:
     655                 :             :                         /* 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   [ +  +  +  + ]:        6420 :         {
     666                 :             :                 termPQExpBuffer(&mybuf);
     667                 :        3678 :                 return NULL;
     668                 :        3678 :         }
     669                 :             : 
     670                 :             :         /* Else return the completed string. */
     671                 :             :         return mybuf.data;
     672                 :        2742 : }
     673                 :        6420 : 
     674                 :             : /*
     675                 :             :  * Eat up any unused \\ to complete a backslash command.
     676                 :             :  */
     677                 :             : void
     678                 :             : psql_scan_slash_command_end(PsqlScanState state)
     679                 :        2664 : {
     680                 :             :         /* Must be scanning already */
     681                 :             :         Assert(state->scanbufhandle != NULL);
     682         [ +  - ]:        2664 : 
     683                 :             :         /* Set current output target */
     684                 :             :         state->output_buf = NULL;    /* we won't output anything */
     685                 :        2664 : 
     686                 :             :         /* Set input source */
     687                 :             :         if (state->buffer_stack != NULL)
     688         [ -  + ]:        2664 :                 yy_switch_to_buffer(state->buffer_stack->buf, state->scanner);
     689                 :           0 :         else
     690                 :             :                 yy_switch_to_buffer(state->scanbufhandle, state->scanner);
     691                 :        2664 : 
     692                 :             :         /* Set lexer start state */
     693                 :             :         state->start_state = xslashend;
     694                 :        2664 : 
     695                 :             :         /* And lex. */
     696                 :             :         yylex(NULL, state->scanner);
     697                 :        2664 : 
     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                 :        2664 : }
     706                 :        2664 : 
     707                 :             : /*
     708                 :             :  * Fetch current paren nesting depth
     709                 :             :  */
     710                 :             : int
     711                 :             : psql_scan_get_paren_depth(PsqlScanState state)
     712                 :          42 : {
     713                 :             :         return state->paren_depth;
     714                 :          42 : }
     715                 :             : 
     716                 :             : /*
     717                 :             :  * Set paren nesting depth
     718                 :             :  */
     719                 :             : void
     720                 :             : psql_scan_set_paren_depth(PsqlScanState state, int depth)
     721                 :          35 : {
     722                 :             :         Assert(depth >= 0);
     723         [ +  - ]:          35 :         state->paren_depth = depth;
     724                 :          35 : }
     725                 :          35 : 
     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                 :          60 : {
     745                 :             :         bool            inquotes = false;
     746                 :          60 :         char       *cp = str;
     747                 :          60 : 
     748                 :             :         while (*cp)
     749         [ +  + ]:         170 :         {
     750                 :             :                 if (*cp == '"')
     751         [ +  + ]:         110 :                 {
     752                 :             :                         if (inquotes && cp[1] == '"')
     753   [ +  +  +  + ]:          14 :                         {
     754                 :             :                                 /* Keep the first quote, remove the second */
     755                 :             :                                 cp++;
     756                 :           2 :                         }
     757                 :           2 :                         else
     758                 :             :                                 inquotes = !inquotes;
     759                 :          12 :                         /* Collapse out quote at *cp */
     760                 :             :                         memmove(cp, cp + 1, strlen(cp));
     761                 :          14 :                         /* do not advance cp */
     762                 :             :                 }
     763                 :          14 :                 else
     764                 :             :                 {
     765                 :             :                         if (downcase && !inquotes)
     766   [ +  +  +  + ]:          96 :                                 *cp = pg_tolower((unsigned char) *cp);
     767                 :          41 :                         cp += PQmblenBounded(cp, encoding);
     768                 :          96 :                 }
     769                 :             :         }
     770                 :             : }
     771                 :          60 : 
     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                 :           0 :         FILE       *fd;
     785                 :           0 :         bool            error = false;
     786                 :           0 :         int                     exit_code = 0;
     787                 :           0 :         char            buf[512];
     788                 :           0 :         size_t          result;
     789                 :           0 : 
     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                 :           0 : 
     801                 :             :         if (!error)
     802         [ #  # ]:           0 :         {
     803                 :             :                 do
     804                 :           0 :                 {
     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                 :           0 : 
     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                 :           0 :         }
     830                 :           0 : 
     831                 :             :         if (PQExpBufferDataBroken(cmd_output))
     832         [ #  # ]:           0 :         {
     833                 :             :                 pg_log_error("%s: out of memory", cmd);
     834                 :           0 :                 error = true;
     835                 :           0 :         }
     836                 :           0 : 
     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                 :           0 : 
     851                 :             :         /* And finally, set the shell result variables */
     852                 :             :         SetShellResultVariables(exit_code);
     853                 :           0 : 
     854                 :             :         termPQExpBuffer(&cmd_output);
     855                 :           0 : }
     856                 :           0 : /* /Users/rhaas/pgsql/src/bin/psql/psqlscanslash.l not long enough */
        

Generated by: LCOV version 2.3.2-1