LCOV - code coverage report
Current view: top level - src/bin/psql - psqlscanslash.l (source / functions) Coverage Total Hit
Test: PostgreSQL 20devel Lines: 66.7 % 321 214
Test Date: 2026-09-01 15:16:46 Functions: 85.7 % 7 6
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 57.6 % 99 57

             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                 :      154844 :         PQExpBuffer output_buf = cur_state->output_buf;
     120                 :      154844 : 
     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                 :      154844 : %}
     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                 :       29228 : 
     145                 :             : {space}|"\\"  {
     146                 :             :                     yyless(0);
     147                 :       29228 :                     cur_state->start_state = YY_START;
     148                 :       29228 :                     return LEXRES_OK;
     149                 :       29228 :                 }
     150                 :             : 
     151                 :             : {other}         { ECHO; }
     152                 :      129036 : 
     153                 :      129036 : }
     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                 :       32782 : 
     162                 :             : {space}+        { }
     163                 :             : 
     164                 :       32782 : "|"               {
     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                 :       33458 :                     yyless(0);
     181                 :       33458 :                     BEGIN(xslasharg);
     182                 :       33458 :                 }
     183                 :             : 
     184                 :       33458 : }
     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                 :        5338 : 
     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                 :        5338 :                     cur_state->start_state = YY_START;
     207                 :        5338 :                     return LEXRES_OK;
     208                 :        5338 :                 }
     209                 :             : 
     210                 :             : {quote}         {
     211                 :       20988 :                     *option_quote = '\'';
     212                 :       20988 :                     unquoted_option_chars = 0;
     213                 :       20988 :                     BEGIN(xslashquote);
     214                 :       20988 :                 }
     215                 :             : 
     216                 :       20988 : "`"               {
     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                 :       92250 :                     unquoted_option_chars++;
     316                 :       92250 :                     ECHO;
     317                 :       92250 :                 }
     318                 :             : 
     319                 :       92250 : }
     320                 :             : 
     321                 :             : <xslashquote>{
     322                 :             :     /*
     323                 :             :      * single-quoted text: copy literally except for '' and backslash
     324                 :             :      * sequences
     325                 :             :      */
     326                 :       20988 : 
     327                 :             : {quote}         { BEGIN(xslasharg); }
     328                 :       20988 : 
     329                 :       20988 : {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                 :      737909 : 
     353                 :      737909 : }
     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                 :         808 : 
     427                 :             : {space}+        {
     428                 :             :                     if (output_buf->len > 0)
     429         [ +  + ]:         808 :                         ECHO;
     430                 :         464 :                 }
     431                 :             : 
     432                 :         808 : {other}         { ECHO; }
     433                 :        9795 : 
     434                 :        9795 : }
     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                 :      119534 : <<EOF>>         {
     453                 :             :                     if (cur_state->buffer_stack == NULL)
     454         [ +  - ]:      119534 :                     {
     455                 :             :                         cur_state->start_state = YY_START;
     456                 :      119534 :                         return LEXRES_EOL;      /* end of input reached */
     457                 :      119534 :                     }
     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, or NULL on out-of-memory.
     478                 :             :  */
     479                 :             : char *
     480                 :             : psql_scan_slash_command(PsqlScanState state)
     481                 :       32485 : {
     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                 :       32485 : 
     490                 :             :     /* Set current output target */
     491                 :             :     state->output_buf = &mybuf;
     492                 :       32485 : 
     493                 :             :     /* Set input source */
     494                 :             :     if (state->buffer_stack != NULL)
     495         [ -  + ]:       32485 :         yy_switch_to_buffer(state->buffer_stack->buf, state->scanner);
     496                 :           0 :     else
     497                 :             :         yy_switch_to_buffer(state->scanbufhandle, state->scanner);
     498                 :       32485 : 
     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                 :       32485 : 
     505                 :             :     /* And lex. */
     506                 :             :     yylex(NULL, state->scanner);
     507                 :       32485 : 
     508                 :             :     /* There are no possible syntax 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                 :       32485 : 
     516                 :             :     /*
     517                 :             :      * yylex() appends command-name text to mybuf, so a buffer enlargement
     518                 :             :      * failure during lexing can leave mybuf broken even if initialization
     519                 :             :      * succeeded.
     520                 :             :      */
     521                 :             :     if (PQExpBufferDataBroken(mybuf))
     522         [ -  + ]:       32485 :     {
     523                 :             :         pg_log_error("out of memory");
     524                 :           0 :         return NULL;
     525                 :           0 :     }
     526                 :             : 
     527                 :             :     return mybuf.data;
     528                 :       32485 : }
     529                 :             : 
     530                 :             : /*
     531                 :             :  * Parse off the next argument for a backslash command, and return it as a
     532                 :             :  * malloc'd string.  If there are no more arguments or on out-of-memory,
     533                 :             :  * returns NULL.
     534                 :             :  *
     535                 :             :  * type tells what processing, if any, to perform on the option string;
     536                 :             :  * for example, if it's a SQL identifier, we want to downcase any unquoted
     537                 :             :  * letters.
     538                 :             :  *
     539                 :             :  * if quote is not NULL, *quote is set to 0 if no quoting was found, else
     540                 :             :  * the last quote symbol used in the argument.
     541                 :             :  *
     542                 :             :  * if semicolon is true, unquoted trailing semicolon(s) that would otherwise
     543                 :             :  * be taken as part of the option string will be stripped.
     544                 :             :  *
     545                 :             :  * NOTE: the only possible syntax errors for backslash options are unmatched
     546                 :             :  * quotes, which are detected when we run out of input.  Therefore, on a
     547                 :             :  * syntax error we just throw away the string and return NULL; there is no
     548                 :             :  * need to worry about flushing remaining input.
     549                 :             :  */
     550                 :             : char *
     551                 :             : psql_scan_slash_option(PsqlScanState state,
     552                 :       89876 :                        enum slash_option_type type,
     553                 :             :                        char *quote,
     554                 :             :                        bool semicolon)
     555                 :             : {
     556                 :             :     PQExpBufferData mybuf;
     557                 :             :     int         lexresult PG_USED_FOR_ASSERTS_ONLY;
     558                 :             :     int         final_state;
     559                 :             :     char        local_quote;
     560                 :             : 
     561                 :             :     /* Must be scanning already */
     562                 :             :     Assert(state->scanbufhandle != NULL);
     563                 :             : 
     564                 :             :     if (quote == NULL)
     565         [ +  + ]:       89876 :         quote = &local_quote;
     566                 :       47497 :     *quote = 0;
     567                 :       89876 : 
     568                 :             :     /* Build a local buffer that we'll return the data of */
     569                 :             :     initPQExpBuffer(&mybuf);
     570                 :       89876 : 
     571                 :             :     /* Set up static variables that will be used by yylex */
     572                 :             :     option_type = type;
     573                 :       89876 :     option_quote = quote;
     574                 :       89876 :     unquoted_option_chars = 0;
     575                 :       89876 : 
     576                 :             :     /* Set current output target */
     577                 :             :     state->output_buf = &mybuf;
     578                 :       89876 : 
     579                 :             :     /* Set input source */
     580                 :             :     if (state->buffer_stack != NULL)
     581         [ -  + ]:       89876 :         yy_switch_to_buffer(state->buffer_stack->buf, state->scanner);
     582                 :           0 :     else
     583                 :             :         yy_switch_to_buffer(state->scanbufhandle, state->scanner);
     584                 :       89876 : 
     585                 :             :     /* Set lexer start state */
     586                 :             :     if (type == OT_WHOLE_LINE)
     587         [ +  + ]:       89876 :         state->start_state = xslashwholeline;
     588                 :        1142 :     else
     589                 :             :         state->start_state = xslashargstart;
     590                 :       88734 : 
     591                 :             :     /* And lex. */
     592                 :             :     lexresult = yylex(NULL, state->scanner);
     593                 :       89876 : 
     594                 :             :     /* Save final state for a moment... */
     595                 :             :     final_state = state->start_state;
     596                 :       89876 : 
     597                 :             :     /*
     598                 :             :      * In case the caller returns to using the regular SQL lexer, reselect the
     599                 :             :      * appropriate initial state.
     600                 :             :      */
     601                 :             :     psql_scan_reselect_sql_lexer(state);
     602                 :       89876 : 
     603                 :             :     /*
     604                 :             :      * Check the lex result: we should have gotten back either LEXRES_OK or
     605                 :             :      * LEXRES_EOL (the latter indicating end of string).  If we were inside a
     606                 :             :      * quoted string, as indicated by final_state, EOL is an error.
     607                 :             :      */
     608                 :             :     Assert(lexresult == LEXRES_EOL || lexresult == LEXRES_OK);
     609                 :             : 
     610                 :             :     /*
     611                 :             :      * yylex() appends option text to mybuf, so a buffer enlargement failure
     612                 :             :      * during lexing can leave mybuf broken even if initialization succeeded.
     613                 :             :      */
     614                 :             :     if (PQExpBufferDataBroken(mybuf))
     615         [ -  + ]:       89876 :     {
     616                 :             :         pg_log_error("out of memory");
     617                 :           0 :         return NULL;
     618                 :           0 :     }
     619                 :             : 
     620                 :             :     switch (final_state)
     621   [ +  +  -  +  :       89876 :     {
                      - ]
     622                 :             :         case xslashargstart:
     623                 :       55256 :             /* empty arg */
     624                 :             :             break;
     625                 :       55256 :         case xslasharg:
     626                 :       33470 :             /* Strip any unquoted trailing semicolons if requested */
     627                 :             :             if (semicolon)
     628         [ +  + ]:       33470 :             {
     629                 :             :                 while (unquoted_option_chars-- > 0 &&
     630                 :        4997 :                        mybuf.len > 0 &&
     631   [ +  +  +  - ]:        5005 :                        mybuf.data[mybuf.len - 1] == ';')
     632         [ +  + ]:        4435 :                 {
     633                 :             :                     mybuf.data[--mybuf.len] = '\0';
     634                 :           8 :                 }
     635                 :             :             }
     636                 :             : 
     637                 :             :             /*
     638                 :             :              * If SQL identifier processing was requested, then we strip out
     639                 :             :              * excess double quotes and optionally downcase unquoted letters.
     640                 :             :              */
     641                 :             :             if (type == OT_SQLID || type == OT_SQLIDHACK)
     642   [ +  +  +  + ]:       33470 :             {
     643                 :             :                 dequote_downcase_identifier(mybuf.data,
     644                 :         174 :                                             (type != OT_SQLIDHACK),
     645                 :             :                                             state->encoding);
     646                 :             :                 /* update mybuf.len for possible shortening */
     647                 :             :                 mybuf.len = strlen(mybuf.data);
     648                 :         174 :             }
     649                 :             :             break;
     650                 :       33470 :         case xslashquote:
     651                 :           0 :         case xslashbackquote:
     652                 :             :         case xslashdquote:
     653                 :             :             /* must have hit EOL inside quotes */
     654                 :             :             pg_log_error("unterminated quoted string");
     655                 :           0 :             termPQExpBuffer(&mybuf);
     656                 :           0 :             return NULL;
     657                 :           0 :         case xslashwholeline:
     658                 :        1150 : 
     659                 :             :             /*
     660                 :             :              * In whole-line mode, we interpret semicolon = true as stripping
     661                 :             :              * trailing whitespace as well as semicolons; this gives the
     662                 :             :              * nearest equivalent to what semicolon = true does in normal
     663                 :             :              * mode.  Note there's no concept of quoting in this mode.
     664                 :             :              */
     665                 :             :             if (semicolon)
     666         [ +  + ]:        1150 :             {
     667                 :             :                 while (mybuf.len > 0 &&
     668         [ +  + ]:         229 :                        (mybuf.data[mybuf.len - 1] == ';' ||
     669         [ +  + ]:         228 :                         (isascii((unsigned char) mybuf.data[mybuf.len - 1]) &&
     670         [ +  - ]:         204 :                          isspace((unsigned char) mybuf.data[mybuf.len - 1]))))
     671         [ -  + ]:         204 :                 {
     672                 :             :                     mybuf.data[--mybuf.len] = '\0';
     673                 :          24 :                 }
     674                 :             :             }
     675                 :             :             break;
     676                 :        1150 :         default:
     677                 :           0 :             /* can't get here */
     678                 :             :             fprintf(stderr, "invalid YY_START\n");
     679                 :           0 :             exit(1);
     680                 :           0 :     }
     681                 :             : 
     682                 :             :     /*
     683                 :             :      * An unquoted empty argument isn't possible unless we are at end of
     684                 :             :      * command.  Return NULL instead.
     685                 :             :      */
     686                 :             :     if (mybuf.len == 0 && *quote == 0)
     687   [ +  +  +  + ]:       89876 :     {
     688                 :             :         termPQExpBuffer(&mybuf);
     689                 :       57462 :         return NULL;
     690                 :       57462 :     }
     691                 :             : 
     692                 :             :     /* Else return the completed string. */
     693                 :             :     return mybuf.data;
     694                 :       32414 : }
     695                 :             : 
     696                 :             : /*
     697                 :             :  * Eat up any unused \\ to complete a backslash command.
     698                 :             :  */
     699                 :             : void
     700                 :             : psql_scan_slash_command_end(PsqlScanState state)
     701                 :       32483 : {
     702                 :             :     /* Must be scanning already */
     703                 :             :     Assert(state->scanbufhandle != NULL);
     704                 :             : 
     705                 :             :     /* Set current output target */
     706                 :             :     state->output_buf = NULL;    /* we won't output anything */
     707                 :       32483 : 
     708                 :             :     /* Set input source */
     709                 :             :     if (state->buffer_stack != NULL)
     710         [ -  + ]:       32483 :         yy_switch_to_buffer(state->buffer_stack->buf, state->scanner);
     711                 :           0 :     else
     712                 :             :         yy_switch_to_buffer(state->scanbufhandle, state->scanner);
     713                 :       32483 : 
     714                 :             :     /* Set lexer start state */
     715                 :             :     state->start_state = xslashend;
     716                 :       32483 : 
     717                 :             :     /* And lex. */
     718                 :             :     yylex(NULL, state->scanner);
     719                 :       32483 : 
     720                 :             :     /* There are no possible errors in this lex state... */
     721                 :             : 
     722                 :             :     /*
     723                 :             :      * We expect the caller to return to using the regular SQL lexer, so
     724                 :             :      * reselect the appropriate initial state.
     725                 :             :      */
     726                 :             :     psql_scan_reselect_sql_lexer(state);
     727                 :       32483 : }
     728                 :       32483 : 
     729                 :             : /*
     730                 :             :  * Save current lexer state
     731                 :             :  *
     732                 :             :  * Relevant parts of the state are returned in a pg_malloc'd struct.
     733                 :             :  * It is caller's responsibility to free the struct eventually.
     734                 :             :  */
     735                 :             : PsqlScanStateSave *
     736                 :             : psql_scan_get_lex_state(PsqlScanState state)
     737                 :         200 : {
     738                 :             :     PsqlScanStateSave *lex_state = pg_malloc_object(PsqlScanStateSave);
     739                 :         200 :     StaticAssertDecl(sizeof(lex_state->init_idents) == sizeof(state->init_idents),
     740                 :             :                      "init_idents array lengths must match");
     741                 :             :     StaticAssertDecl(sizeof(lex_state->sub_idents) == sizeof(state->sub_idents),
     742                 :             :                      "sub_idents array lengths must match");
     743                 :             : 
     744                 :             :     lex_state->paren_depth = state->paren_depth;
     745                 :         200 :     lex_state->begin_depth = state->begin_depth;
     746                 :         200 :     lex_state->copy_stdin_count = state->copy_stdin_count;
     747                 :         200 :     lex_state->init_idents_count = state->init_idents_count;
     748                 :         200 :     memcpy(lex_state->init_idents, state->init_idents,
     749                 :         200 :            sizeof(lex_state->init_idents));
     750                 :             :     lex_state->sub_idents_count = state->sub_idents_count;
     751                 :         200 :     memcpy(lex_state->sub_idents, state->sub_idents,
     752                 :         200 :            sizeof(lex_state->sub_idents));
     753                 :             :     return lex_state;
     754                 :         200 : }
     755                 :             : 
     756                 :             : /*
     757                 :             :  * Restore lexer state to what it was when saved
     758                 :             :  */
     759                 :             : void
     760                 :             : psql_scan_set_lex_state(PsqlScanState state,
     761                 :         169 :                         const PsqlScanStateSave *lex_state)
     762                 :             : {
     763                 :             :     state->paren_depth = lex_state->paren_depth;
     764                 :         169 :     state->begin_depth = lex_state->begin_depth;
     765                 :         169 :     state->copy_stdin_count = lex_state->copy_stdin_count;
     766                 :         169 :     state->init_idents_count = lex_state->init_idents_count;
     767                 :         169 :     memcpy(state->init_idents, lex_state->init_idents,
     768                 :         169 :            sizeof(state->init_idents));
     769                 :             :     state->sub_idents_count = lex_state->sub_idents_count;
     770                 :         169 :     memcpy(state->sub_idents, lex_state->sub_idents,
     771                 :         169 :            sizeof(state->sub_idents));
     772                 :             : }
     773                 :         169 : 
     774                 :             : /*
     775                 :             :  * De-quote and optionally downcase a SQL identifier.
     776                 :             :  *
     777                 :             :  * The string at *str is modified in-place; it can become shorter,
     778                 :             :  * but not longer.
     779                 :             :  *
     780                 :             :  * If downcase is true then non-quoted letters are folded to lower case.
     781                 :             :  * Ideally this behavior will match the backend's downcase_identifier();
     782                 :             :  * but note that it could differ if LC_CTYPE is different in the frontend.
     783                 :             :  *
     784                 :             :  * Note that a string like FOO"BAR"BAZ will be converted to fooBARbaz;
     785                 :             :  * this is somewhat inconsistent with the SQL spec, which would have us
     786                 :             :  * parse it as several identifiers.  But for psql's purposes, we want a
     787                 :             :  * string like "foo"."bar" to be treated as one option, so there's little
     788                 :             :  * choice; this routine doesn't get to change the token boundaries.
     789                 :             :  */
     790                 :             : void
     791                 :             : dequote_downcase_identifier(char *str, bool downcase, int encoding)
     792                 :         322 : {
     793                 :             :     bool        inquotes = false;
     794                 :         322 :     char       *cp = str;
     795                 :         322 : 
     796                 :             :     while (*cp)
     797         [ +  + ]:        2118 :     {
     798                 :             :         if (*cp == '"')
     799         [ +  + ]:        1796 :         {
     800                 :             :             if (inquotes && cp[1] == '"')
     801   [ +  +  +  + ]:          82 :             {
     802                 :             :                 /* Keep the first quote, remove the second */
     803                 :             :                 cp++;
     804                 :          12 :             }
     805                 :             :             else
     806                 :             :                 inquotes = !inquotes;
     807                 :          70 :             /* Collapse out quote at *cp */
     808                 :             :             memmove(cp, cp + 1, strlen(cp));
     809                 :          82 :             /* do not advance cp */
     810                 :             :         }
     811                 :             :         else
     812                 :             :         {
     813                 :             :             if (downcase && !inquotes)
     814   [ +  +  +  + ]:        1714 :                 *cp = pg_tolower((unsigned char) *cp);
     815                 :         295 :             cp += PQmblenBounded(cp, encoding);
     816                 :        1714 :         }
     817                 :             :     }
     818                 :             : }
     819                 :         322 : 
     820                 :             : /*
     821                 :             :  * Evaluate a backticked substring of a slash command's argument.
     822                 :             :  *
     823                 :             :  * The portion of output_buf starting at backtick_start_offset is evaluated
     824                 :             :  * as a shell command and then replaced by the command's output.
     825                 :             :  */
     826                 :             : static void
     827                 :             : evaluate_backtick(PsqlScanState state)
     828                 :           0 : {
     829                 :             :     PQExpBuffer output_buf = state->output_buf;
     830                 :           0 :     char       *cmd;
     831                 :             :     PQExpBufferData cmd_output;
     832                 :             :     FILE       *fd;
     833                 :             :     bool        error = false;
     834                 :           0 :     int         exit_code = 0;
     835                 :           0 :     char        buf[512];
     836                 :             :     size_t      result;
     837                 :             : 
     838                 :             :     /*
     839                 :             :      * The option buffer is already broken; avoid touching the static
     840                 :             :      * oom_buffer and let psql_scan_slash_option() return NULL.
     841                 :             :      */
     842                 :             :     if (PQExpBufferBroken(output_buf))
     843   [ #  #  #  # ]:           0 :         return;
     844                 :           0 : 
     845                 :             :     cmd = output_buf->data + backtick_start_offset;
     846                 :           0 : 
     847                 :             :     initPQExpBuffer(&cmd_output);
     848                 :           0 : 
     849                 :             :     fflush(NULL);
     850                 :           0 :     fd = popen(cmd, "r");
     851                 :           0 :     if (!fd)
     852         [ #  # ]:           0 :     {
     853                 :             :         pg_log_error("%s: %m", cmd);
     854                 :           0 :         error = true;
     855                 :           0 :         exit_code = -1;
     856                 :           0 :     }
     857                 :             : 
     858                 :             :     if (!error)
     859         [ #  # ]:           0 :     {
     860                 :             :         do
     861                 :             :         {
     862                 :             :             result = fread(buf, 1, sizeof(buf), fd);
     863                 :           0 :             if (ferror(fd))
     864         [ #  # ]:           0 :             {
     865                 :             :                 pg_log_error("%s: %m", cmd);
     866                 :           0 :                 error = true;
     867                 :           0 :                 break;
     868                 :           0 :             }
     869                 :             :             appendBinaryPQExpBuffer(&cmd_output, buf, result);
     870                 :           0 :         } while (!feof(fd));
     871         [ #  # ]:           0 :     }
     872                 :             : 
     873                 :             :     if (fd)
     874         [ #  # ]:           0 :     {
     875                 :             :         /*
     876                 :             :          * Although pclose's result always sets the shell result variables, we
     877                 :             :          * historically have abandoned the backtick substitution only if it
     878                 :             :          * returns -1.
     879                 :             :          */
     880                 :             :         exit_code = pclose(fd);
     881                 :           0 :         if (exit_code == -1)
     882         [ #  # ]:           0 :         {
     883                 :             :             pg_log_error("%s: %m", cmd);
     884                 :           0 :             error = true;
     885                 :           0 :         }
     886                 :             :     }
     887                 :             : 
     888                 :             :     if (PQExpBufferDataBroken(cmd_output))
     889         [ #  # ]:           0 :     {
     890                 :             :         pg_log_error("%s: out of memory", cmd);
     891                 :           0 :         error = true;
     892                 :           0 :     }
     893                 :             : 
     894                 :             :     /* Now done with cmd, delete it from output_buf */
     895                 :             :     output_buf->len = backtick_start_offset;
     896                 :           0 :     output_buf->data[output_buf->len] = '\0';
     897                 :           0 : 
     898                 :             :     /* If no error, transfer result to output_buf */
     899                 :             :     if (!error)
     900         [ #  # ]:           0 :     {
     901                 :             :         /* strip any trailing newline (but only one) */
     902                 :             :         if (cmd_output.len > 0 &&
     903         [ #  # ]:           0 :             cmd_output.data[cmd_output.len - 1] == '\n')
     904         [ #  # ]:           0 :             cmd_output.len--;
     905                 :           0 :         appendBinaryPQExpBuffer(output_buf, cmd_output.data, cmd_output.len);
     906                 :           0 :     }
     907                 :             : 
     908                 :             :     /* And finally, set the shell result variables */
     909                 :             :     SetShellResultVariables(exit_code);
     910                 :           0 : 
     911                 :             :     termPQExpBuffer(&cmd_output);
     912                 :           0 : }
     913                 :             : /* END: function "evaluate_backtick" */
        

Generated by: LCOV version 2.0-1