YugabyteDB (2.13.1.0-b60, 21121d69985fbf76aa6958d8f04a9bfa936293b5)

Coverage Report

Created: 2022-03-22 16:43

/Users/deen/code/yugabyte-db/src/postgres/src/bin/psql/mainloop.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * psql - the PostgreSQL interactive terminal
3
 *
4
 * Copyright (c) 2000-2018, PostgreSQL Global Development Group
5
 *
6
 * src/bin/psql/mainloop.c
7
 */
8
#include "postgres_fe.h"
9
#include "mainloop.h"
10
11
#include "command.h"
12
#include "common.h"
13
#include "input.h"
14
#include "prompt.h"
15
#include "settings.h"
16
17
#include "mb/pg_wchar.h"
18
19
20
/* callback functions for our flex lexer */
21
const PsqlScanCallbacks psqlscan_callbacks = {
22
  psql_get_variable,
23
  psql_error
24
};
25
26
27
/*
28
 * Main processing loop for reading lines of input
29
 *  and sending them to the backend.
30
 *
31
 * This loop is re-entrant. May be called by \i command
32
 *  which reads input from a file.
33
 */
34
int
35
MainLoop(FILE *source)
36
99
{
37
99
  PsqlScanState scan_state; /* lexer working state */
38
99
  ConditionalStack cond_stack;  /* \if status stack */
39
99
  volatile PQExpBuffer query_buf; /* buffer for query being accumulated */
40
99
  volatile PQExpBuffer previous_buf;  /* if there isn't anything in the new
41
                     * buffer yet, use this one for \e,
42
                     * etc. */
43
99
  PQExpBuffer history_buf;  /* earlier lines of a multi-line command, not
44
                 * yet saved to readline history */
45
99
  char     *line;     /* current line of input */
46
99
  int     added_nl_pos;
47
99
  bool    success;
48
99
  bool    line_saved_in_history;
49
99
  volatile int successResult = EXIT_SUCCESS;
50
99
  volatile backslashResult slashCmdStatus = PSQL_CMD_UNKNOWN;
51
99
  volatile promptStatus_t prompt_status = PROMPT_READY;
52
99
  volatile int count_eof = 0;
53
99
  volatile bool die_on_error = false;
54
99
  FILE     *prev_cmd_source;
55
99
  bool    prev_cmd_interactive;
56
99
  uint64    prev_lineno;
57
58
  /* Save the prior command source */
59
99
  prev_cmd_source = pset.cur_cmd_source;
60
99
  prev_cmd_interactive = pset.cur_cmd_interactive;
61
99
  prev_lineno = pset.lineno;
62
  /* pset.stmt_lineno does not need to be saved and restored */
63
64
  /* Establish new source */
65
99
  pset.cur_cmd_source = source;
66
99
  pset.cur_cmd_interactive = ((source == stdin) && 
!pset.notty83
);
67
99
  pset.lineno = 0;
68
99
  pset.stmt_lineno = 1;
69
70
  /* Create working state */
71
99
  scan_state = psql_scan_create(&psqlscan_callbacks);
72
99
  cond_stack = conditional_stack_create();
73
99
  psql_scan_set_passthrough(scan_state, (void *) cond_stack);
74
75
99
  query_buf = createPQExpBuffer();
76
99
  previous_buf = createPQExpBuffer();
77
99
  history_buf = createPQExpBuffer();
78
99
  if (PQExpBufferBroken(query_buf) ||
79
99
    PQExpBufferBroken(previous_buf) ||
80
99
    PQExpBufferBroken(history_buf))
81
0
  {
82
0
    psql_error("out of memory\n");
83
0
    exit(EXIT_FAILURE);
84
0
  }
85
86
  /* main loop to get queries and execute them */
87
14.7k
  
while (99
successResult == EXIT_SUCCESS)
88
14.7k
  {
89
    /*
90
     * Clean up after a previous Control-C
91
     */
92
14.7k
    if (cancel_pressed)
93
0
    {
94
0
      if (!pset.cur_cmd_interactive)
95
0
      {
96
        /*
97
         * You get here if you stopped a script with Ctrl-C.
98
         */
99
0
        successResult = EXIT_USER;
100
0
        break;
101
0
      }
102
103
0
      cancel_pressed = false;
104
0
    }
105
106
    /*
107
     * Establish longjmp destination for exiting from wait-for-input. We
108
     * must re-do this each time through the loop for safety, since the
109
     * jmpbuf might get changed during command execution.
110
     */
111
14.7k
    if (sigsetjmp(sigint_interrupt_jmp, 1) != 0)
112
0
    {
113
      /* got here with longjmp */
114
115
      /* reset parsing state */
116
0
      psql_scan_finish(scan_state);
117
0
      psql_scan_reset(scan_state);
118
0
      resetPQExpBuffer(query_buf);
119
0
      resetPQExpBuffer(history_buf);
120
0
      count_eof = 0;
121
0
      slashCmdStatus = PSQL_CMD_UNKNOWN;
122
0
      prompt_status = PROMPT_READY;
123
0
      pset.stmt_lineno = 1;
124
0
      cancel_pressed = false;
125
126
0
      if (pset.cur_cmd_interactive)
127
0
      {
128
0
        putc('\n', stdout);
129
130
        /*
131
         * if interactive user is in an \if block, then Ctrl-C will
132
         * exit from the innermost \if.
133
         */
134
0
        if (!conditional_stack_empty(cond_stack))
135
0
        {
136
0
          psql_error("\\if: escaped\n");
137
0
          conditional_stack_pop(cond_stack);
138
0
        }
139
0
      }
140
0
      else
141
0
      {
142
0
        successResult = EXIT_USER;
143
0
        break;
144
0
      }
145
0
    }
146
147
14.7k
    fflush(stdout);
148
149
    /*
150
     * get another line
151
     */
152
14.7k
    if (pset.cur_cmd_interactive)
153
0
    {
154
      /* May need to reset prompt, eg after \r command */
155
0
      if (query_buf->len == 0)
156
0
        prompt_status = PROMPT_READY;
157
0
      line = gets_interactive(get_prompt(prompt_status, cond_stack),
158
0
                  query_buf);
159
0
    }
160
14.7k
    else
161
14.7k
    {
162
14.7k
      line = gets_fromFile(source);
163
14.7k
      if (!line && 
ferror(source)99
)
164
0
        successResult = EXIT_FAILURE;
165
14.7k
    }
166
167
    /*
168
     * query_buf holds query already accumulated.  line is the malloc'd
169
     * new line of input (note it must be freed before looping around!)
170
     */
171
172
    /* No more input.  Time to quit, or \i done */
173
14.7k
    if (line == NULL)
174
99
    {
175
99
      if (pset.cur_cmd_interactive)
176
0
      {
177
        /* This tries to mimic bash's IGNOREEOF feature. */
178
0
        count_eof++;
179
180
0
        if (count_eof < pset.ignoreeof)
181
0
        {
182
0
          if (!pset.quiet)
183
0
            printf(_("Use \"\\q\" to leave %s.\n"), pset.progname);
184
0
          continue;
185
0
        }
186
187
0
        puts(pset.quiet ? "" : "\\q");
188
0
      }
189
99
      break;
190
99
    }
191
192
14.6k
    count_eof = 0;
193
194
14.6k
    pset.lineno++;
195
196
    /* ignore UTF-8 Unicode byte-order mark */
197
14.6k
    if (pset.lineno == 1 && 
pset.encoding == PG_UTF899
&&
strncmp(line, "\xef\xbb\xbf", 3) == 099
)
198
0
      memmove(line, line + 3, strlen(line + 3) + 1);
199
200
    /* Detect attempts to run custom-format dumps as SQL scripts */
201
14.6k
    if (pset.lineno == 1 && 
!pset.cur_cmd_interactive99
&&
202
14.6k
      
strncmp(line, "PGDMP", 5) == 099
)
203
0
    {
204
0
      free(line);
205
0
      puts(_("The input is a PostgreSQL custom-format dump.\n"
206
0
           "Use the pg_restore command-line client to restore this dump to a database.\n"));
207
0
      fflush(stdout);
208
0
      successResult = EXIT_FAILURE;
209
0
      break;
210
0
    }
211
212
    /* no further processing of empty lines, unless within a literal */
213
14.6k
    if (line[0] == '\0' && 
!psql_scan_in_quote(scan_state)2.11k
)
214
2.04k
    {
215
2.04k
      free(line);
216
2.04k
      continue;
217
2.04k
    }
218
219
    /* Recognize "help", "quit", "exit" only in interactive mode */
220
12.6k
    if (pset.cur_cmd_interactive)
221
0
    {
222
0
      char     *first_word = line;
223
0
      char     *rest_of_line = NULL;
224
0
      bool    found_help = false;
225
0
      bool    found_exit_or_quit = false;
226
0
      bool    found_q = false;
227
228
      /* Search for the words we recognize;  must be first word */
229
0
      if (pg_strncasecmp(first_word, "help", 4) == 0)
230
0
      {
231
0
        rest_of_line = first_word + 4;
232
0
        found_help = true;
233
0
      }
234
0
      else if (pg_strncasecmp(first_word, "exit", 4) == 0 ||
235
0
           pg_strncasecmp(first_word, "quit", 4) == 0)
236
0
      {
237
0
        rest_of_line = first_word + 4;
238
0
        found_exit_or_quit = true;
239
0
      }
240
241
0
      else if (strncmp(first_word, "\\q", 2) == 0)
242
0
      {
243
0
        rest_of_line = first_word + 2;
244
0
        found_q = true;
245
0
      }
246
247
      /*
248
       * If we found a command word, check whether the rest of the line
249
       * contains only whitespace plus maybe one semicolon.  If not,
250
       * ignore the command word after all.  These commands are only for
251
       * compatibility with other SQL clients and are not documented.
252
       */
253
0
      if (rest_of_line != NULL)
254
0
      {
255
        /*
256
         * Ignore unless rest of line is whitespace, plus maybe one
257
         * semicolon
258
         */
259
0
        while (isspace((unsigned char) *rest_of_line))
260
0
          ++rest_of_line;
261
0
        if (*rest_of_line == ';')
262
0
          ++rest_of_line;
263
0
        while (isspace((unsigned char) *rest_of_line))
264
0
          ++rest_of_line;
265
0
        if (*rest_of_line != '\0')
266
0
        {
267
0
          found_help = false;
268
0
          found_exit_or_quit = false;
269
0
        }
270
0
      }
271
272
      /*
273
       * "help" is only a command when the query buffer is empty, but we
274
       * emit a one-line message even when it isn't to help confused
275
       * users.  The text is still added to the query buffer in that
276
       * case.
277
       */
278
0
      if (found_help)
279
0
      {
280
0
        if (query_buf->len != 0)
281
0
#ifndef WIN32
282
0
          puts(_("Use \\? for help or press control-C to clear the input buffer."));
283
#else
284
          puts(_("Use \\? for help."));
285
#endif
286
0
        else
287
0
        {
288
0
          puts(_("You are using psql, the command-line interface to PostgreSQL."));
289
0
          printf(_("Type:  \\copyright for distribution terms\n"
290
0
               "       \\h for help with SQL commands\n"
291
0
               "       \\? for help with psql commands\n"
292
0
               "       \\g or terminate with semicolon to execute query\n"
293
0
               "       \\q to quit\n"));
294
0
          free(line);
295
0
          fflush(stdout);
296
0
          continue;
297
0
        }
298
0
      }
299
300
      /*
301
       * "quit" and "exit" are only commands when the query buffer is
302
       * empty, but we emit a one-line message even when it isn't to
303
       * help confused users.  The text is still added to the query
304
       * buffer in that case.
305
       */
306
0
      if (found_exit_or_quit)
307
0
      {
308
0
        if (query_buf->len != 0)
309
0
        {
310
0
          if (prompt_status == PROMPT_READY ||
311
0
            prompt_status == PROMPT_CONTINUE ||
312
0
            prompt_status == PROMPT_PAREN)
313
0
            puts(_("Use \\q to quit."));
314
0
          else
315
0
#ifndef WIN32
316
0
            puts(_("Use control-D to quit."));
317
#else
318
            puts(_("Use control-C to quit."));
319
#endif
320
0
        }
321
0
        else
322
0
        {
323
          /* exit app */
324
0
          free(line);
325
0
          fflush(stdout);
326
0
          successResult = EXIT_SUCCESS;
327
0
          break;
328
0
        }
329
0
      }
330
331
      /*
332
       * If they typed "\q" in a place where "\q" is not active, supply
333
       * a hint.  The text is still added to the query buffer.
334
       */
335
0
      if (found_q && query_buf->len != 0 &&
336
0
        prompt_status != PROMPT_READY &&
337
0
        prompt_status != PROMPT_CONTINUE &&
338
0
        prompt_status != PROMPT_PAREN)
339
0
#ifndef WIN32
340
0
        puts(_("Use control-D to quit."));
341
#else
342
        puts(_("Use control-C to quit."));
343
#endif
344
0
    }
345
346
    /* echo back if flag is set, unless interactive */
347
12.6k
    if (pset.echo == PSQL_ECHO_ALL && 
!pset.cur_cmd_interactive12.4k
)
348
12.4k
    {
349
12.4k
      puts(line);
350
12.4k
      fflush(stdout);
351
12.4k
    }
352
353
    /* insert newlines into query buffer between source lines */
354
12.6k
    if (query_buf->len > 0)
355
3.13k
    {
356
3.13k
      appendPQExpBufferChar(query_buf, '\n');
357
3.13k
      added_nl_pos = query_buf->len;
358
3.13k
    }
359
9.48k
    else
360
9.48k
      added_nl_pos = -1; /* flag we didn't add one */
361
362
    /* Setting this will not have effect until next line. */
363
12.6k
    die_on_error = pset.on_error_stop;
364
365
    /*
366
     * Parse line, looking for command separators.
367
     */
368
12.6k
    psql_scan_setup(scan_state, line, strlen(line),
369
12.6k
            pset.encoding, standard_strings());
370
12.6k
    success = true;
371
12.6k
    line_saved_in_history = false;
372
373
19.5k
    while (success || 
!die_on_error879
)
374
19.5k
    {
375
19.5k
      PsqlScanResult scan_result;
376
19.5k
      promptStatus_t prompt_tmp = prompt_status;
377
19.5k
      size_t    pos_in_query;
378
19.5k
      char     *tmp_line;
379
380
19.5k
      pos_in_query = query_buf->len;
381
19.5k
      scan_result = psql_scan(scan_state, query_buf, &prompt_tmp);
382
19.5k
      prompt_status = prompt_tmp;
383
384
19.5k
      if (PQExpBufferBroken(query_buf))
385
0
      {
386
0
        psql_error("out of memory\n");
387
0
        exit(EXIT_FAILURE);
388
0
      }
389
390
      /*
391
       * Increase statement line number counter for each linebreak added
392
       * to the query buffer by the last psql_scan() call. There only
393
       * will be ones to add when navigating to a statement in
394
       * readline's history containing newlines.
395
       */
396
19.5k
      tmp_line = query_buf->data + pos_in_query;
397
755k
      while (*tmp_line != '\0')
398
735k
      {
399
735k
        if (*(tmp_line++) == '\n')
400
0
          pset.stmt_lineno++;
401
735k
      }
402
403
19.5k
      if (scan_result == PSCAN_EOL)
404
896
        pset.stmt_lineno++;
405
406
      /*
407
       * Send command if semicolon found, or if end of line and we're in
408
       * single-line mode.
409
       */
410
19.5k
      if (scan_result == PSCAN_SEMICOLON ||
411
19.5k
        
(12.6k
scan_result == PSCAN_EOL12.6k
&&
pset.singleline896
))
412
6.89k
      {
413
        /*
414
         * Save line in history.  We use history_buf to accumulate
415
         * multi-line queries into a single history entry.  Note that
416
         * history accumulation works on input lines, so it doesn't
417
         * matter whether the query will be ignored due to \if.
418
         */
419
6.89k
        if (pset.cur_cmd_interactive && 
!line_saved_in_history0
)
420
0
        {
421
0
          pg_append_history(line, history_buf);
422
0
          pg_send_history(history_buf);
423
0
          line_saved_in_history = true;
424
0
        }
425
426
        /* execute query unless we're in an inactive \if branch */
427
6.89k
        if (conditional_active(cond_stack))
428
6.89k
        {
429
6.89k
          success = SendQuery(query_buf->data);
430
6.89k
          slashCmdStatus = success ? 
PSQL_CMD_SEND6.01k
:
PSQL_CMD_ERROR878
;
431
6.89k
          pset.stmt_lineno = 1;
432
433
          /* transfer query to previous_buf by pointer-swapping */
434
6.89k
          {
435
6.89k
            PQExpBuffer swap_buf = previous_buf;
436
437
6.89k
            previous_buf = query_buf;
438
6.89k
            query_buf = swap_buf;
439
6.89k
          }
440
6.89k
          resetPQExpBuffer(query_buf);
441
442
6.89k
          added_nl_pos = -1;
443
          /* we need not do psql_scan_reset() here */
444
6.89k
        }
445
0
        else
446
0
        {
447
          /* if interactive, warn about non-executed query */
448
0
          if (pset.cur_cmd_interactive)
449
0
            psql_error("query ignored; use \\endif or Ctrl-C to exit current \\if block\n");
450
          /* fake an OK result for purposes of loop checks */
451
0
          success = true;
452
0
          slashCmdStatus = PSQL_CMD_SEND;
453
0
          pset.stmt_lineno = 1;
454
          /* note that query_buf doesn't change state */
455
0
        }
456
6.89k
      }
457
12.6k
      else if (scan_result == PSCAN_BACKSLASH)
458
60
      {
459
        /* handle backslash command */
460
461
        /*
462
         * If we added a newline to query_buf, and nothing else has
463
         * been inserted in query_buf by the lexer, then strip off the
464
         * newline again.  This avoids any change to query_buf when a
465
         * line contains only a backslash command.  Also, in this
466
         * situation we force out any previous lines as a separate
467
         * history entry; we don't want SQL and backslash commands
468
         * intermixed in history if at all possible.
469
         */
470
60
        if (query_buf->len == added_nl_pos)
471
0
        {
472
0
          query_buf->data[--query_buf->len] = '\0';
473
0
          pg_send_history(history_buf);
474
0
        }
475
60
        added_nl_pos = -1;
476
477
        /* save backslash command in history */
478
60
        if (pset.cur_cmd_interactive && 
!line_saved_in_history0
)
479
0
        {
480
0
          pg_append_history(line, history_buf);
481
0
          pg_send_history(history_buf);
482
0
          line_saved_in_history = true;
483
0
        }
484
485
        /* execute backslash command */
486
60
        slashCmdStatus = HandleSlashCmds(scan_state,
487
60
                         cond_stack,
488
60
                         query_buf,
489
60
                         previous_buf);
490
491
60
        success = slashCmdStatus != PSQL_CMD_ERROR;
492
493
        /*
494
         * Resetting stmt_lineno after a backslash command isn't
495
         * always appropriate, but it's what we've done historically
496
         * and there have been few complaints.
497
         */
498
60
        pset.stmt_lineno = 1;
499
500
60
        if (slashCmdStatus == PSQL_CMD_SEND)
501
2
        {
502
          /* should not see this in inactive branch */
503
2
          Assert(conditional_active(cond_stack));
504
505
0
          success = SendQuery(query_buf->data);
506
507
          /* transfer query to previous_buf by pointer-swapping */
508
2
          {
509
2
            PQExpBuffer swap_buf = previous_buf;
510
511
2
            previous_buf = query_buf;
512
2
            query_buf = swap_buf;
513
2
          }
514
2
          resetPQExpBuffer(query_buf);
515
516
          /* flush any paren nesting info after forced send */
517
2
          psql_scan_reset(scan_state);
518
2
        }
519
58
        else if (slashCmdStatus == PSQL_CMD_NEWEDIT)
520
0
        {
521
          /* should not see this in inactive branch */
522
0
          Assert(conditional_active(cond_stack));
523
          /* rescan query_buf as new input */
524
0
          psql_scan_finish(scan_state);
525
0
          free(line);
526
0
          line = pg_strdup(query_buf->data);
527
0
          resetPQExpBuffer(query_buf);
528
          /* reset parsing state since we are rescanning whole line */
529
0
          psql_scan_reset(scan_state);
530
0
          psql_scan_setup(scan_state, line, strlen(line),
531
0
                  pset.encoding, standard_strings());
532
0
          line_saved_in_history = false;
533
0
          prompt_status = PROMPT_READY;
534
0
        }
535
58
        else if (slashCmdStatus == PSQL_CMD_TERMINATE)
536
0
          break;
537
60
      }
538
539
      /* fall out of loop if lexer reached EOL */
540
19.5k
      if (scan_result == PSCAN_INCOMPLETE ||
541
19.5k
        
scan_result == PSCAN_EOL7.85k
)
542
12.6k
        break;
543
19.5k
    }
544
545
    /* Add line to pending history if we didn't execute anything yet */
546
12.6k
    if (pset.cur_cmd_interactive && 
!line_saved_in_history0
)
547
0
      pg_append_history(line, history_buf);
548
549
12.6k
    psql_scan_finish(scan_state);
550
12.6k
    free(line);
551
552
12.6k
    if (slashCmdStatus == PSQL_CMD_TERMINATE)
553
0
    {
554
0
      successResult = EXIT_SUCCESS;
555
0
      break;
556
0
    }
557
558
12.6k
    if (!pset.cur_cmd_interactive)
559
12.6k
    {
560
12.6k
      if (!success && 
die_on_error879
)
561
0
        successResult = EXIT_USER;
562
      /* Have we lost the db connection? */
563
12.6k
      else if (!pset.db)
564
0
        successResult = EXIT_BADCONN;
565
12.6k
    }
566
12.6k
  }              /* while !endoffile/session */
567
568
  /*
569
   * If we have a non-semicolon-terminated query at the end of file, we
570
   * process it unless the input source is interactive --- in that case it
571
   * seems better to go ahead and quit.  Also skip if this is an error exit.
572
   */
573
99
  if (query_buf->len > 0 && 
!pset.cur_cmd_interactive16
&&
574
99
    
successResult == EXIT_SUCCESS16
)
575
16
  {
576
    /* save query in history */
577
    /* currently unneeded since we don't use this block if interactive */
578
#ifdef NOT_USED
579
    if (pset.cur_cmd_interactive)
580
      pg_send_history(history_buf);
581
#endif
582
583
    /* execute query unless we're in an inactive \if branch */
584
16
    if (conditional_active(cond_stack))
585
16
    {
586
16
      success = SendQuery(query_buf->data);
587
16
    }
588
0
    else
589
0
    {
590
0
      if (pset.cur_cmd_interactive)
591
0
        psql_error("query ignored; use \\endif or Ctrl-C to exit current \\if block\n");
592
0
      success = true;
593
0
    }
594
595
16
    if (!success && 
die_on_error0
)
596
0
      successResult = EXIT_USER;
597
16
    else if (pset.db == NULL)
598
0
      successResult = EXIT_BADCONN;
599
16
  }
600
601
  /*
602
   * Check for unbalanced \if-\endifs unless user explicitly quit, or the
603
   * script is erroring out
604
   */
605
99
  if (slashCmdStatus != PSQL_CMD_TERMINATE &&
606
99
    successResult != EXIT_USER &&
607
99
    !conditional_stack_empty(cond_stack))
608
0
  {
609
0
    psql_error("reached EOF without finding closing \\endif(s)\n");
610
0
    if (die_on_error && !pset.cur_cmd_interactive)
611
0
      successResult = EXIT_USER;
612
0
  }
613
614
  /*
615
   * Let's just make real sure the SIGINT handler won't try to use
616
   * sigint_interrupt_jmp after we exit this routine.  If there is an outer
617
   * MainLoop instance, it will reset sigint_interrupt_jmp to point to
618
   * itself at the top of its loop, before any further interactive input
619
   * happens.
620
   */
621
99
  sigint_interrupt_enabled = false;
622
623
99
  destroyPQExpBuffer(query_buf);
624
99
  destroyPQExpBuffer(previous_buf);
625
99
  destroyPQExpBuffer(history_buf);
626
627
99
  psql_scan_destroy(scan_state);
628
99
  conditional_stack_destroy(cond_stack);
629
630
99
  pset.cur_cmd_source = prev_cmd_source;
631
99
  pset.cur_cmd_interactive = prev_cmd_interactive;
632
99
  pset.lineno = prev_lineno;
633
634
99
  return successResult;
635
99
}                /* MainLoop() */