PostgreSQL Source Code  git master
mainloop.h File Reference
Include dependency graph for mainloop.h:
This graph shows which files directly or indirectly include this file:

Go to the source code of this file.

Functions

int MainLoop (FILE *source)
 

Variables

const PsqlScanCallbacks psqlscan_callbacks
 

Function Documentation

◆ MainLoop()

int MainLoop ( FILE *  source)

Definition at line 35 of file mainloop.c.

References _, appendPQExpBufferChar(), Assert, cancel_pressed, conditional_active(), conditional_stack_create(), conditional_stack_destroy(), conditional_stack_empty(), conditional_stack_pop(), createPQExpBuffer(), _psqlSettings::cur_cmd_interactive, _psqlSettings::cur_cmd_source, PQExpBufferData::data, _psqlSettings::db, destroyPQExpBuffer(), _psqlSettings::echo, _psqlSettings::encoding, EXIT_BADCONN, EXIT_FAILURE, EXIT_SUCCESS, EXIT_USER, free, get_prompt(), gets_fromFile(), gets_interactive(), HandleSlashCmds(), _psqlSettings::ignoreeof, PQExpBufferData::len, _psqlSettings::lineno, memmove, _psqlSettings::notty, _psqlSettings::on_error_stop, pg_append_history(), pg_send_history(), pg_strdup(), pg_strncasecmp(), PG_UTF8, PQExpBufferBroken, _psqlSettings::progname, PROMPT_CONTINUE, PROMPT_PAREN, PROMPT_READY, PSCAN_BACKSLASH, PSCAN_EOL, PSCAN_INCOMPLETE, PSCAN_SEMICOLON, pset, PSQL_CMD_ERROR, PSQL_CMD_NEWEDIT, PSQL_CMD_SEND, PSQL_CMD_TERMINATE, PSQL_CMD_UNKNOWN, PSQL_ECHO_ALL, psql_error(), psql_scan(), psql_scan_create(), psql_scan_destroy(), psql_scan_finish(), psql_scan_in_quote(), psql_scan_reset(), psql_scan_set_passthrough(), psql_scan_setup(), _psqlSettings::quiet, resetPQExpBuffer(), SendQuery(), sigint_interrupt_enabled, sigint_interrupt_jmp, _psqlSettings::singleline, standard_strings(), _psqlSettings::stmt_lineno, and success.

Referenced by main(), and process_file().

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

Variable Documentation

◆ psqlscan_callbacks

const PsqlScanCallbacks psqlscan_callbacks

Definition at line 21 of file mainloop.c.

Referenced by main().