LCOV - code coverage report
Current view: top level - src/bin/psql - psqlscanslash.l (source / functions) Coverage Total Hit
Test: PostgreSQL 20devel Lines: 66.0 % 300 198
Test Date: 2026-07-23 07:15:41 Functions: 85.7 % 7 6
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 60.4 % 91 55

             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                 :      154385 :         PQExpBuffer output_buf = cur_state->output_buf;
     120                 :      154385 : 
     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                 :      154385 : %}
     130                 :             : 
     131                 :             :     /*
     132                 :             :      * We don't really expect to be invoked in the INITIAL state in this
     133                 :             :      * lexer; but if we are, just spit data to the output_buf until EOF.
     134                 :             :      */
     135                 :             : 
     136                 :             : {other}|\n      { ECHO; }
     137                 :           0 : 
     138                 :           0 :     /*
     139                 :             :      * Exclusive lexer states to handle backslash command lexing
     140                 :             :      */
     141                 :             : 
     142                 :             : <xslashcmd>{
     143                 :             :     /* command name ends at whitespace or backslash; eat all else */
     144                 :       29164 : 
     145                 :             : {space}|"\\"  {
     146                 :             :                     yyless(0);
     147                 :       29164 :                     cur_state->start_state = YY_START;
     148                 :       29164 :                     return LEXRES_OK;
     149                 :       29164 :                 }
     150                 :             : 
     151                 :             : {other}         { ECHO; }
     152                 :      128423 : 
     153                 :      128423 : }
     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                 :       32763 : 
     162                 :             : {space}+        { }
     163                 :             : 
     164                 :       32763 : "|"               {
     165                 :          20 :                     if (option_type == OT_FILEPIPE)
     166         [ +  + ]:          20 :                     {
     167                 :             :                         /* treat like whole-string case */
     168                 :             :                         ECHO;
     169                 :           8 :                         BEGIN(xslashwholeline);
     170                 :           8 :                     }
     171                 :             :                     else
     172                 :             :                     {
     173                 :             :                         /* vertical bar is not special otherwise */
     174                 :             :                         yyless(0);
     175                 :          12 :                         BEGIN(xslasharg);
     176                 :          12 :                     }
     177                 :             :                 }
     178                 :             : 
     179                 :          20 : {other}         {
     180                 :       33439 :                     yyless(0);
     181                 :       33439 :                     BEGIN(xslasharg);
     182                 :       33439 :                 }
     183                 :             : 
     184                 :       33439 : }
     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                 :        5334 : 
     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                 :        5334 :                     cur_state->start_state = YY_START;
     207                 :        5334 :                     return LEXRES_OK;
     208                 :        5334 :                 }
     209                 :             : 
     210                 :             : {quote}         {
     211                 :       20910 :                     *option_quote = '\'';
     212                 :       20910 :                     unquoted_option_chars = 0;
     213                 :       20910 :                     BEGIN(xslashquote);
     214                 :       20910 :                 }
     215                 :             : 
     216                 :       20910 : "`"               {
     217                 :          12 :                     backtick_start_offset = output_buf->len;
     218                 :          12 :                     *option_quote = '`';
     219                 :          12 :                     unquoted_option_chars = 0;
     220                 :          12 :                     BEGIN(xslashbackquote);
     221                 :          12 :                 }
     222                 :             : 
     223                 :          12 : {dquote}        {
     224                 :         991 :                     ECHO;
     225                 :         991 :                     *option_quote = '"';
     226                 :         991 :                     unquoted_option_chars = 0;
     227                 :         991 :                     BEGIN(xslashdquote);
     228                 :         991 :                 }
     229                 :             : 
     230                 :         991 : :{variable_char}+   {
     231                 :         862 :                     /* Possible psql variable substitution */
     232                 :             :                     if (cur_state->callbacks->get_variable == NULL)
     233         [ -  + ]:         862 :                         ECHO;
     234                 :           0 :                     else
     235                 :             :                     {
     236                 :             :                         char       *varname;
     237                 :             :                         char       *value;
     238                 :             : 
     239                 :             :                         varname = psqlscan_extract_substring(cur_state,
     240                 :         862 :                                                              yytext + 1,
     241                 :         862 :                                                              yyleng - 1);
     242                 :         862 :                         value = cur_state->callbacks->get_variable(varname,
     243                 :         862 :                                                                    PQUOTE_PLAIN,
     244                 :             :                                                                    cur_state->cb_passthrough);
     245                 :             :                         free(varname);
     246                 :         862 : 
     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         [ +  + ]:         862 :                         {
     256                 :             :                             appendPQExpBufferStr(output_buf, value);
     257                 :         838 :                             free(value);
     258                 :         838 :                         }
     259                 :             :                         else
     260                 :             :                             ECHO;
     261                 :          24 : 
     262                 :             :                         *option_quote = ':';
     263                 :         862 :                     }
     264                 :             :                     unquoted_option_chars = 0;
     265                 :         862 :                 }
     266                 :             : 
     267                 :         862 : :'{variable_char}+' {
     268                 :          28 :                     psqlscan_escape_variable(cur_state, yytext, yyleng,
     269                 :          28 :                                              PQUOTE_SQL_LITERAL);
     270                 :             :                     *option_quote = ':';
     271                 :          28 :                     unquoted_option_chars = 0;
     272                 :          28 :                 }
     273                 :             : 
     274                 :          28 : 
     275                 :          16 : :\"{variable_char}+\" {
     276                 :             :                     psqlscan_escape_variable(cur_state, yytext, yyleng,
     277                 :          16 :                                              PQUOTE_SQL_IDENT);
     278                 :             :                     *option_quote = ':';
     279                 :          16 :                     unquoted_option_chars = 0;
     280                 :          16 :                 }
     281                 :             : 
     282                 :          16 : :\{\?{variable_char}+\} {
     283                 :          13 :                     psqlscan_test_variable(cur_state, yytext, yyleng);
     284                 :          13 :                 }
     285                 :             : 
     286                 :          13 : :'{variable_char}*  {
     287                 :           0 :                     /* Throw back everything but the colon */
     288                 :             :                     yyless(1);
     289                 :           0 :                     unquoted_option_chars++;
     290                 :           0 :                     ECHO;
     291                 :           0 :                 }
     292                 :             : 
     293                 :           0 : :\"{variable_char}*    {
     294                 :           0 :                     /* Throw back everything but the colon */
     295                 :             :                     yyless(1);
     296                 :           0 :                     unquoted_option_chars++;
     297                 :           0 :                     ECHO;
     298                 :           0 :                 }
     299                 :             : 
     300                 :           0 : :\{\?{variable_char}*   {
     301                 :           0 :                     /* Throw back everything but the colon */
     302                 :             :                     yyless(1);
     303                 :           0 :                     unquoted_option_chars++;
     304                 :           0 :                     ECHO;
     305                 :           0 :                 }
     306                 :             : 
     307                 :           0 : :\{     {
     308                 :           0 :                     /* Throw back everything but the colon */
     309                 :             :                     yyless(1);
     310                 :           0 :                     unquoted_option_chars++;
     311                 :           0 :                     ECHO;
     312                 :           0 :                 }
     313                 :             : 
     314                 :           0 : {other}         {
     315                 :       94385 :                     unquoted_option_chars++;
     316                 :       94385 :                     ECHO;
     317                 :       94385 :                 }
     318                 :             : 
     319                 :       94385 : }
     320                 :             : 
     321                 :             : <xslashquote>{
     322                 :             :     /*
     323                 :             :      * single-quoted text: copy literally except for '' and backslash
     324                 :             :      * sequences
     325                 :             :      */
     326                 :       20910 : 
     327                 :             : {quote}         { BEGIN(xslasharg); }
     328                 :       20910 : 
     329                 :       20910 : {xqdouble}      { appendPQExpBufferChar(output_buf, '\''); }
     330                 :          58 : 
     331                 :          58 : "\\n"         { appendPQExpBufferChar(output_buf, '\n'); }
     332                 :           4 : "\\t"         { appendPQExpBufferChar(output_buf, '\t'); }
     333                 :           4 : "\\b"         { appendPQExpBufferChar(output_buf, '\b'); }
     334                 :           4 : "\\r"         { appendPQExpBufferChar(output_buf, '\r'); }
     335                 :           0 : "\\f"         { appendPQExpBufferChar(output_buf, '\f'); }
     336                 :           4 : 
     337                 :           0 : {xeoctesc}      {
     338                 :           4 :                     /* octal case */
     339                 :             :                     appendPQExpBufferChar(output_buf,
     340                 :           4 :                                           (char) strtol(yytext + 1, NULL, 8));
     341                 :           4 :                 }
     342                 :             : 
     343                 :           4 : {xehexesc}      {
     344                 :           0 :                     /* hex case */
     345                 :             :                     appendPQExpBufferChar(output_buf,
     346                 :           0 :                                           (char) strtol(yytext + 2, NULL, 16));
     347                 :           0 :                 }
     348                 :             : 
     349                 :           0 : "\\".         { psqlscan_emit(cur_state, yytext + 1, 1); }
     350                 :          12 : 
     351                 :          12 : {other}|\n      { ECHO; }
     352                 :      735569 : 
     353                 :      735569 : }
     354                 :             : 
     355                 :             : <xslashbackquote>{
     356                 :             :     /*
     357                 :             :      * backticked text: copy everything until next backquote (expanding
     358                 :             :      * variable references, but doing nought else), then evaluate.
     359                 :             :      */
     360                 :          12 : 
     361                 :             : "`"               {
     362                 :             :                     /* In an inactive \if branch, don't evaluate the command */
     363                 :             :                     if (cur_state->cb_passthrough == NULL ||
     364   [ +  -  -  + ]:          24 :                         conditional_active((ConditionalStack) cur_state->cb_passthrough))
     365                 :          12 :                         evaluate_backtick(cur_state);
     366                 :           0 :                     BEGIN(xslasharg);
     367                 :          12 :                 }
     368                 :             : 
     369                 :          12 : :{variable_char}+   {
     370                 :           0 :                     /* Possible psql variable substitution */
     371                 :             :                     if (cur_state->callbacks->get_variable == NULL)
     372         [ #  # ]:           0 :                         ECHO;
     373                 :           0 :                     else
     374                 :             :                     {
     375                 :             :                         char       *varname;
     376                 :             :                         char       *value;
     377                 :             : 
     378                 :             :                         varname = psqlscan_extract_substring(cur_state,
     379                 :           0 :                                                              yytext + 1,
     380                 :           0 :                                                              yyleng - 1);
     381                 :           0 :                         value = cur_state->callbacks->get_variable(varname,
     382                 :           0 :                                                                    PQUOTE_PLAIN,
     383                 :             :                                                                    cur_state->cb_passthrough);
     384                 :             :                         free(varname);
     385                 :           0 : 
     386                 :             :                         if (value)
     387         [ #  # ]:           0 :                         {
     388                 :             :                             appendPQExpBufferStr(output_buf, value);
     389                 :           0 :                             free(value);
     390                 :           0 :                         }
     391                 :             :                         else
     392                 :             :                             ECHO;
     393                 :           0 :                     }
     394                 :             :                 }
     395                 :             : 
     396                 :           0 : :'{variable_char}+' {
     397                 :           0 :                     psqlscan_escape_variable(cur_state, yytext, yyleng,
     398                 :           0 :                                              PQUOTE_SHELL_ARG);
     399                 :             :                 }
     400                 :             : 
     401                 :           0 : :'{variable_char}*  {
     402                 :           0 :                     /* Throw back everything but the colon */
     403                 :             :                     yyless(1);
     404                 :           0 :                     ECHO;
     405                 :           0 :                 }
     406                 :             : 
     407                 :           0 : {other}|\n      { ECHO; }
     408                 :         156 : 
     409                 :         156 : }
     410                 :             : 
     411                 :             : <xslashdquote>{
     412                 :             :     /* double-quoted text: copy verbatim, including the double quotes */
     413                 :         991 : 
     414                 :             : {dquote}        {
     415                 :             :                     ECHO;
     416                 :         991 :                     BEGIN(xslasharg);
     417                 :         991 :                 }
     418                 :             : 
     419                 :         991 : {other}|\n      { ECHO; }
     420                 :       18127 : 
     421                 :       18127 : }
     422                 :             : 
     423                 :             : <xslashwholeline>{
     424                 :             :     /* copy everything until end of input line */
     425                 :             :     /* but suppress leading whitespace */
     426                 :         750 : 
     427                 :             : {space}+        {
     428                 :             :                     if (output_buf->len > 0)
     429         [ +  + ]:         750 :                         ECHO;
     430                 :         455 :                 }
     431                 :             : 
     432                 :         750 : {other}         { ECHO; }
     433                 :        7473 : 
     434                 :        7473 : }
     435                 :             : 
     436                 :             : <xslashend>{
     437                 :             :     /* at end of command, eat a double backslash, but not anything else */
     438                 :          46 : 
     439                 :             : "\\\\"            {
     440                 :             :                     cur_state->start_state = YY_START;
     441                 :          46 :                     return LEXRES_OK;
     442                 :          46 :                 }
     443                 :             : 
     444                 :             : {other}|\n      {
     445                 :         698 :                     yyless(0);
     446                 :         698 :                     cur_state->start_state = YY_START;
     447                 :         698 :                     return LEXRES_OK;
     448                 :         698 :                 }
     449                 :             : 
     450                 :             : }
     451                 :             : 
     452                 :      119143 : <<EOF>>         {
     453                 :             :                     if (cur_state->buffer_stack == NULL)
     454         [ +  - ]:      119143 :                     {
     455                 :             :                         cur_state->start_state = YY_START;
     456                 :      119143 :                         return LEXRES_EOL;      /* end of input reached */
     457                 :      119143 :                     }
     458                 :             : 
     459                 :             :                     /*
     460                 :             :                      * We were expanding a variable, so pop the inclusion
     461                 :             :                      * stack and keep lexing
     462                 :             :                      */
     463                 :             :                     psqlscan_pop_buffer_stack(cur_state);
     464                 :           0 :                     psqlscan_select_top_buffer(cur_state);
     465                 :           0 :                 }
     466                 :             : 
     467                 :           0 : %%
     468                 :           0 : 
     469                 :             : /* LCOV_EXCL_STOP */
     470                 :             : 
     471                 :             : /*
     472                 :             :  * Scan the command name of a psql backslash command.  This should be called
     473                 :             :  * after psql_scan() returns PSCAN_BACKSLASH.  It is assumed that the input
     474                 :             :  * has been consumed through the leading backslash.
     475                 :             :  *
     476                 :             :  * The return value is a malloc'd copy of the command name, as parsed off
     477                 :             :  * from the input.
     478                 :             :  */
     479                 :             : char *
     480                 :             : psql_scan_slash_command(PsqlScanState state)
     481                 :       32388 : {
     482                 :             :     PQExpBufferData mybuf;
     483                 :             : 
     484                 :             :     /* Must be scanning already */
     485                 :             :     Assert(state->scanbufhandle != NULL);
     486                 :             : 
     487                 :             :     /* Build a local buffer that we'll return the data of */
     488                 :             :     initPQExpBuffer(&mybuf);
     489                 :       32388 : 
     490                 :             :     /* Set current output target */
     491                 :             :     state->output_buf = &mybuf;
     492                 :       32388 : 
     493                 :             :     /* Set input source */
     494                 :             :     if (state->buffer_stack != NULL)
     495         [ -  + ]:       32388 :         yy_switch_to_buffer(state->buffer_stack->buf, state->scanner);
     496                 :           0 :     else
     497                 :             :         yy_switch_to_buffer(state->scanbufhandle, state->scanner);
     498                 :       32388 : 
     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                 :       32388 : 
     505                 :             :     /* And lex. */
     506                 :             :     yylex(NULL, state->scanner);
     507                 :       32388 : 
     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                 :       32388 : 
     516                 :             :     return mybuf.data;
     517                 :       32388 : }
     518                 :             : 
     519                 :             : /*
     520                 :             :  * Parse off the next argument for a backslash command, and return it as a
     521                 :             :  * malloc'd string.  If there are no more arguments, returns NULL.
     522                 :             :  *
     523                 :             :  * type tells what processing, if any, to perform on the option string;
     524                 :             :  * for example, if it's a SQL identifier, we want to downcase any unquoted
     525                 :             :  * letters.
     526                 :             :  *
     527                 :             :  * if quote is not NULL, *quote is set to 0 if no quoting was found, else
     528                 :             :  * the last quote symbol used in the argument.
     529                 :             :  *
     530                 :             :  * if semicolon is true, unquoted trailing semicolon(s) that would otherwise
     531                 :             :  * be taken as part of the option string will be stripped.
     532                 :             :  *
     533                 :             :  * NOTE: the only possible syntax errors for backslash options are unmatched
     534                 :             :  * quotes, which are detected when we run out of input.  Therefore, on a
     535                 :             :  * syntax error we just throw away the string and return NULL; there is no
     536                 :             :  * need to worry about flushing remaining input.
     537                 :             :  */
     538                 :             : char *
     539                 :             : psql_scan_slash_option(PsqlScanState state,
     540                 :       89611 :                        enum slash_option_type type,
     541                 :             :                        char *quote,
     542                 :             :                        bool semicolon)
     543                 :             : {
     544                 :             :     PQExpBufferData mybuf;
     545                 :             :     int         lexresult PG_USED_FOR_ASSERTS_ONLY;
     546                 :             :     int         final_state;
     547                 :             :     char        local_quote;
     548                 :             : 
     549                 :             :     /* Must be scanning already */
     550                 :             :     Assert(state->scanbufhandle != NULL);
     551                 :             : 
     552                 :             :     if (quote == NULL)
     553         [ +  + ]:       89611 :         quote = &local_quote;
     554                 :       47440 :     *quote = 0;
     555                 :       89611 : 
     556                 :             :     /* Build a local buffer that we'll return the data of */
     557                 :             :     initPQExpBuffer(&mybuf);
     558                 :       89611 : 
     559                 :             :     /* Set up static variables that will be used by yylex */
     560                 :             :     option_type = type;
     561                 :       89611 :     option_quote = quote;
     562                 :       89611 :     unquoted_option_chars = 0;
     563                 :       89611 : 
     564                 :             :     /* Set current output target */
     565                 :             :     state->output_buf = &mybuf;
     566                 :       89611 : 
     567                 :             :     /* Set input source */
     568                 :             :     if (state->buffer_stack != NULL)
     569         [ -  + ]:       89611 :         yy_switch_to_buffer(state->buffer_stack->buf, state->scanner);
     570                 :           0 :     else
     571                 :             :         yy_switch_to_buffer(state->scanbufhandle, state->scanner);
     572                 :       89611 : 
     573                 :             :     /* Set lexer start state */
     574                 :             :     if (type == OT_WHOLE_LINE)
     575         [ +  + ]:       89611 :         state->start_state = xslashwholeline;
     576                 :        1092 :     else
     577                 :             :         state->start_state = xslashargstart;
     578                 :       88519 : 
     579                 :             :     /* And lex. */
     580                 :             :     lexresult = yylex(NULL, state->scanner);
     581                 :       89611 : 
     582                 :             :     /* Save final state for a moment... */
     583                 :             :     final_state = state->start_state;
     584                 :       89611 : 
     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                 :       89611 : 
     591                 :             :     /*
     592                 :             :      * Check the lex result: we should have gotten back either LEXRES_OK or
     593                 :             :      * LEXRES_EOL (the latter indicating end of string).  If we were inside a
     594                 :             :      * quoted string, as indicated by final_state, EOL is an error.
     595                 :             :      */
     596                 :             :     Assert(lexresult == LEXRES_EOL || lexresult == LEXRES_OK);
     597                 :             : 
     598                 :             :     switch (final_state)
     599   [ +  +  -  +  :       89611 :     {
                      - ]
     600                 :             :         case xslashargstart:
     601                 :       55060 :             /* empty arg */
     602                 :             :             break;
     603                 :       55060 :         case xslasharg:
     604                 :       33451 :             /* Strip any unquoted trailing semicolons if requested */
     605                 :             :             if (semicolon)
     606         [ +  + ]:       33451 :             {
     607                 :             :                 while (unquoted_option_chars-- > 0 &&
     608                 :        5068 :                        mybuf.len > 0 &&
     609   [ +  +  +  - ]:        5076 :                        mybuf.data[mybuf.len - 1] == ';')
     610         [ +  + ]:        4506 :                 {
     611                 :             :                     mybuf.data[--mybuf.len] = '\0';
     612                 :           8 :                 }
     613                 :             :             }
     614                 :             : 
     615                 :             :             /*
     616                 :             :              * If SQL identifier processing was requested, then we strip out
     617                 :             :              * excess double quotes and optionally downcase unquoted letters.
     618                 :             :              */
     619                 :             :             if (type == OT_SQLID || type == OT_SQLIDHACK)
     620   [ +  +  +  + ]:       33451 :             {
     621                 :             :                 dequote_downcase_identifier(mybuf.data,
     622                 :         170 :                                             (type != OT_SQLIDHACK),
     623                 :             :                                             state->encoding);
     624                 :             :                 /* update mybuf.len for possible shortening */
     625                 :             :                 mybuf.len = strlen(mybuf.data);
     626                 :         170 :             }
     627                 :             :             break;
     628                 :       33451 :         case xslashquote:
     629                 :           0 :         case xslashbackquote:
     630                 :             :         case xslashdquote:
     631                 :             :             /* must have hit EOL inside quotes */
     632                 :             :             pg_log_error("unterminated quoted string");
     633                 :           0 :             termPQExpBuffer(&mybuf);
     634                 :           0 :             return NULL;
     635                 :           0 :         case xslashwholeline:
     636                 :        1100 : 
     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         [ +  + ]:        1100 :             {
     645                 :             :                 while (mybuf.len > 0 &&
     646         [ +  + ]:         188 :                        (mybuf.data[mybuf.len - 1] == ';' ||
     647         [ +  + ]:         187 :                         (isascii((unsigned char) mybuf.data[mybuf.len - 1]) &&
     648         [ +  - ]:         163 :                          isspace((unsigned char) mybuf.data[mybuf.len - 1]))))
     649         [ -  + ]:         163 :                 {
     650                 :             :                     mybuf.data[--mybuf.len] = '\0';
     651                 :          24 :                 }
     652                 :             :             }
     653                 :             :             break;
     654                 :        1100 :         default:
     655                 :           0 :             /* can't get here */
     656                 :             :             fprintf(stderr, "invalid YY_START\n");
     657                 :           0 :             exit(1);
     658                 :           0 :     }
     659                 :             : 
     660                 :             :     /*
     661                 :             :      * An unquoted empty argument isn't possible unless we are at end of
     662                 :             :      * command.  Return NULL instead.
     663                 :             :      */
     664                 :             :     if (mybuf.len == 0 && *quote == 0)
     665   [ +  +  +  + ]:       89611 :     {
     666                 :             :         termPQExpBuffer(&mybuf);
     667                 :       57265 :         return NULL;
     668                 :       57265 :     }
     669                 :             : 
     670                 :             :     /* Else return the completed string. */
     671                 :             :     return mybuf.data;
     672                 :       32346 : }
     673                 :             : 
     674                 :             : /*
     675                 :             :  * Eat up any unused \\ to complete a backslash command.
     676                 :             :  */
     677                 :             : void
     678                 :             : psql_scan_slash_command_end(PsqlScanState state)
     679                 :       32386 : {
     680                 :             :     /* Must be scanning already */
     681                 :             :     Assert(state->scanbufhandle != NULL);
     682                 :             : 
     683                 :             :     /* Set current output target */
     684                 :             :     state->output_buf = NULL;    /* we won't output anything */
     685                 :       32386 : 
     686                 :             :     /* Set input source */
     687                 :             :     if (state->buffer_stack != NULL)
     688         [ -  + ]:       32386 :         yy_switch_to_buffer(state->buffer_stack->buf, state->scanner);
     689                 :           0 :     else
     690                 :             :         yy_switch_to_buffer(state->scanbufhandle, state->scanner);
     691                 :       32386 : 
     692                 :             :     /* Set lexer start state */
     693                 :             :     state->start_state = xslashend;
     694                 :       32386 : 
     695                 :             :     /* And lex. */
     696                 :             :     yylex(NULL, state->scanner);
     697                 :       32386 : 
     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                 :       32386 : }
     706                 :       32386 : 
     707                 :             : /*
     708                 :             :  * Fetch current paren nesting depth
     709                 :             :  */
     710                 :             : int
     711                 :             : psql_scan_get_paren_depth(PsqlScanState state)
     712                 :         196 : {
     713                 :             :     return state->paren_depth;
     714                 :         196 : }
     715                 :             : 
     716                 :             : /*
     717                 :             :  * Set paren nesting depth
     718                 :             :  */
     719                 :             : void
     720                 :             : psql_scan_set_paren_depth(PsqlScanState state, int depth)
     721                 :         165 : {
     722                 :             :     Assert(depth >= 0);
     723                 :             :     state->paren_depth = depth;
     724                 :         165 : }
     725                 :         165 : 
     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                 :         318 : {
     745                 :             :     bool        inquotes = false;
     746                 :         318 :     char       *cp = str;
     747                 :         318 : 
     748                 :             :     while (*cp)
     749         [ +  + ]:        2110 :     {
     750                 :             :         if (*cp == '"')
     751         [ +  + ]:        1792 :         {
     752                 :             :             if (inquotes && cp[1] == '"')
     753   [ +  +  +  + ]:          82 :             {
     754                 :             :                 /* Keep the first quote, remove the second */
     755                 :             :                 cp++;
     756                 :          12 :             }
     757                 :             :             else
     758                 :             :                 inquotes = !inquotes;
     759                 :          70 :             /* Collapse out quote at *cp */
     760                 :             :             memmove(cp, cp + 1, strlen(cp));
     761                 :          82 :             /* do not advance cp */
     762                 :             :         }
     763                 :             :         else
     764                 :             :         {
     765                 :             :             if (downcase && !inquotes)
     766   [ +  +  +  + ]:        1710 :                 *cp = pg_tolower((unsigned char) *cp);
     767                 :         295 :             cp += PQmblenBounded(cp, encoding);
     768                 :        1710 :         }
     769                 :             :     }
     770                 :             : }
     771                 :         318 : 
     772                 :             : /*
     773                 :             :  * Evaluate a backticked substring of a slash command's argument.
     774                 :             :  *
     775                 :             :  * The portion of output_buf starting at backtick_start_offset is evaluated
     776                 :             :  * as a shell command and then replaced by the command's output.
     777                 :             :  */
     778                 :             : static void
     779                 :             : evaluate_backtick(PsqlScanState state)
     780                 :           0 : {
     781                 :             :     PQExpBuffer output_buf = state->output_buf;
     782                 :           0 :     char       *cmd = output_buf->data + backtick_start_offset;
     783                 :           0 :     PQExpBufferData cmd_output;
     784                 :             :     FILE       *fd;
     785                 :             :     bool        error = false;
     786                 :           0 :     int         exit_code = 0;
     787                 :           0 :     char        buf[512];
     788                 :             :     size_t      result;
     789                 :             : 
     790                 :             :     initPQExpBuffer(&cmd_output);
     791                 :           0 : 
     792                 :             :     fflush(NULL);
     793                 :           0 :     fd = popen(cmd, "r");
     794                 :           0 :     if (!fd)
     795         [ #  # ]:           0 :     {
     796                 :             :         pg_log_error("%s: %m", cmd);
     797                 :           0 :         error = true;
     798                 :           0 :         exit_code = -1;
     799                 :           0 :     }
     800                 :             : 
     801                 :             :     if (!error)
     802         [ #  # ]:           0 :     {
     803                 :             :         do
     804                 :             :         {
     805                 :             :             result = fread(buf, 1, sizeof(buf), fd);
     806                 :           0 :             if (ferror(fd))
     807         [ #  # ]:           0 :             {
     808                 :             :                 pg_log_error("%s: %m", cmd);
     809                 :           0 :                 error = true;
     810                 :           0 :                 break;
     811                 :           0 :             }
     812                 :             :             appendBinaryPQExpBuffer(&cmd_output, buf, result);
     813                 :           0 :         } while (!feof(fd));
     814         [ #  # ]:           0 :     }
     815                 :             : 
     816                 :             :     if (fd)
     817         [ #  # ]:           0 :     {
     818                 :             :         /*
     819                 :             :          * Although pclose's result always sets the shell result variables, we
     820                 :             :          * historically have abandoned the backtick substitution only if it
     821                 :             :          * returns -1.
     822                 :             :          */
     823                 :             :         exit_code = pclose(fd);
     824                 :           0 :         if (exit_code == -1)
     825         [ #  # ]:           0 :         {
     826                 :             :             pg_log_error("%s: %m", cmd);
     827                 :           0 :             error = true;
     828                 :           0 :         }
     829                 :             :     }
     830                 :             : 
     831                 :             :     if (PQExpBufferDataBroken(cmd_output))
     832         [ #  # ]:           0 :     {
     833                 :             :         pg_log_error("%s: out of memory", cmd);
     834                 :           0 :         error = true;
     835                 :           0 :     }
     836                 :             : 
     837                 :             :     /* Now done with cmd, delete it from output_buf */
     838                 :             :     output_buf->len = backtick_start_offset;
     839                 :           0 :     output_buf->data[output_buf->len] = '\0';
     840                 :           0 : 
     841                 :             :     /* If no error, transfer result to output_buf */
     842                 :             :     if (!error)
     843         [ #  # ]:           0 :     {
     844                 :             :         /* strip any trailing newline (but only one) */
     845                 :             :         if (cmd_output.len > 0 &&
     846         [ #  # ]:           0 :             cmd_output.data[cmd_output.len - 1] == '\n')
     847         [ #  # ]:           0 :             cmd_output.len--;
     848                 :           0 :         appendBinaryPQExpBuffer(output_buf, cmd_output.data, cmd_output.len);
     849                 :           0 :     }
     850                 :             : 
     851                 :             :     /* And finally, set the shell result variables */
     852                 :             :     SetShellResultVariables(exit_code);
     853                 :           0 : 
     854                 :             :     termPQExpBuffer(&cmd_output);
     855                 :           0 : }
     856                 :           0 : /* END: function "evaluate_backtick" */
        

Generated by: LCOV version 2.0-1