Hello,
I give a quick look and I think we can void psql_setup_cancel_handler(void) { setup_cancel_handler(psql_sigint_callback); } Because it does not matter for setup_cancel_handler what we passed because it is ignoring that in case of windows.
The "psql_sigint_callback" function is not defined under WIN32. I've fixed a missing NULL argument in the section you pointed out, though. I've used the shared infrastructure in pgbench.I've noticed yet another instance of the cancelation stuff in "src/bin/pg_dump/parallel.c", but it seems somehow different from the two others, so I have not tried to used the shared version.
-- Fabien.
diff --git a/src/bin/pgbench/pgbench.c b/src/bin/pgbench/pgbench.c index 03bcd22996..389b4d7bcd 100644 --- a/src/bin/pgbench/pgbench.c +++ b/src/bin/pgbench/pgbench.c @@ -59,9 +59,10 @@ #include "common/int.h" #include "common/logging.h" -#include "fe_utils/conditional.h" #include "getopt_long.h" #include "libpq-fe.h" +#include "fe_utils/conditional.h" +#include "fe_utils/cancel.h" #include "pgbench.h" #include "portability/instr_time.h" @@ -3887,6 +3888,9 @@ initGenerateData(PGconn *con) exit(1); } + if (CancelRequested) + break; + /* * If we want to stick with the original logging, print a message each * 100k inserted rows. @@ -4057,6 +4061,9 @@ runInitSteps(const char *initialize_steps) if ((con = doConnect()) == NULL) exit(1); + setup_cancel_handler(NULL); + SetCancelConn(con); + for (step = initialize_steps; *step != '\0'; step++) { instr_time start; @@ -4120,6 +4127,7 @@ runInitSteps(const char *initialize_steps) } fprintf(stderr, "done in %.2f s (%s).\n", run_time, stats.data); + ResetCancelConn(); PQfinish(con); termPQExpBuffer(&stats); } diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c index b981ae81ff..f1d9e0298a 100644 --- a/src/bin/psql/command.c +++ b/src/bin/psql/command.c @@ -29,6 +29,7 @@ #include "libpq-fe.h" #include "pqexpbuffer.h" #include "common/logging.h" +#include "fe_utils/cancel.h" #include "fe_utils/print.h" #include "fe_utils/string_utils.h" diff --git a/src/bin/psql/common.c b/src/bin/psql/common.c index 90f6380170..0a66b71372 100644 --- a/src/bin/psql/common.c +++ b/src/bin/psql/common.c @@ -24,6 +24,7 @@ #include "copy.h" #include "crosstabview.h" #include "fe_utils/mbprint.h" +#include "fe_utils/cancel.h" #include "fe_utils/string_utils.h" #include "portability/instr_time.h" #include "settings.h" @@ -223,6 +224,7 @@ NoticeProcessor(void *arg, const char *message) } +#ifndef WIN32 /* * Code to support query cancellation @@ -241,7 +243,7 @@ NoticeProcessor(void *arg, const char *message) * * SIGINT is supposed to abort all long-running psql operations, not only * database queries. In most places, this is accomplished by checking - * cancel_pressed during long-running loops. However, that won't work when + * CancelRequested during long-running loops. However, that won't work when * blocked on user input (in readline() or fgets()). In those places, we * set sigint_interrupt_enabled true while blocked, instructing the signal * catcher to longjmp through sigint_interrupt_jmp. We assume readline and @@ -252,34 +254,9 @@ volatile bool sigint_interrupt_enabled = false; sigjmp_buf sigint_interrupt_jmp; -static PGcancel *volatile cancelConn = NULL; - -#ifdef WIN32 -static CRITICAL_SECTION cancelConnLock; -#endif - -/* - * Write a simple string to stderr --- must be safe in a signal handler. - * We ignore the write() result since there's not much we could do about it. - * Certain compilers make that harder than it ought to be. - */ -#define write_stderr(str) \ - do { \ - const char *str_ = (str); \ - int rc_; \ - rc_ = write(fileno(stderr), str_, strlen(str_)); \ - (void) rc_; \ - } while (0) - - -#ifndef WIN32 - static void -handle_sigint(SIGNAL_ARGS) +psql_sigint_callback(void) { - int save_errno = errno; - char errbuf[256]; - /* if we are waiting for input, longjmp out of it */ if (sigint_interrupt_enabled) { @@ -288,74 +265,20 @@ handle_sigint(SIGNAL_ARGS) } /* else, set cancel flag to stop any long-running loops */ - cancel_pressed = true; - - /* and send QueryCancel if we are processing a database query */ - if (cancelConn != NULL) - { - if (PQcancel(cancelConn, errbuf, sizeof(errbuf))) - write_stderr("Cancel request sent\n"); - else - { - write_stderr("Could not send cancel request: "); - write_stderr(errbuf); - } - } - - errno = save_errno; /* just in case the write changed it */ + CancelRequested = true; } -void -setup_cancel_handler(void) -{ - pqsignal(SIGINT, handle_sigint); -} -#else /* WIN32 */ - -static BOOL WINAPI -consoleHandler(DWORD dwCtrlType) -{ - char errbuf[256]; - - if (dwCtrlType == CTRL_C_EVENT || - dwCtrlType == CTRL_BREAK_EVENT) - { - /* - * Can't longjmp here, because we are in wrong thread :-( - */ - - /* set cancel flag to stop any long-running loops */ - cancel_pressed = true; - - /* and send QueryCancel if we are processing a database query */ - EnterCriticalSection(&cancelConnLock); - if (cancelConn != NULL) - { - if (PQcancel(cancelConn, errbuf, sizeof(errbuf))) - write_stderr("Cancel request sent\n"); - else - { - write_stderr("Could not send cancel request: "); - write_stderr(errbuf); - } - } - LeaveCriticalSection(&cancelConnLock); - - return TRUE; - } - else - /* Return FALSE for any signals not being handled */ - return FALSE; -} +#endif /* !WIN32 */ void -setup_cancel_handler(void) +psql_setup_cancel_handler(void) { - InitializeCriticalSection(&cancelConnLock); - - SetConsoleCtrlHandler(consoleHandler, TRUE); +#ifndef WIN32 + setup_cancel_handler(psql_sigint_callback); +#else + setup_cancel_handler(NULL); +#endif /* WIN32 */ } -#endif /* WIN32 */ /* ConnectionUp @@ -369,7 +292,6 @@ ConnectionUp(void) } - /* CheckConnection * * Verify that we still have a good connection to the backend, and if not, @@ -428,62 +350,6 @@ CheckConnection(void) -/* - * SetCancelConn - * - * Set cancelConn to point to the current database connection. - */ -void -SetCancelConn(void) -{ - PGcancel *oldCancelConn; - -#ifdef WIN32 - EnterCriticalSection(&cancelConnLock); -#endif - - /* Free the old one if we have one */ - oldCancelConn = cancelConn; - /* be sure handle_sigint doesn't use pointer while freeing */ - cancelConn = NULL; - - if (oldCancelConn != NULL) - PQfreeCancel(oldCancelConn); - - cancelConn = PQgetCancel(pset.db); - -#ifdef WIN32 - LeaveCriticalSection(&cancelConnLock); -#endif -} - - -/* - * ResetCancelConn - * - * Free the current cancel connection, if any, and set to NULL. - */ -void -ResetCancelConn(void) -{ - PGcancel *oldCancelConn; - -#ifdef WIN32 - EnterCriticalSection(&cancelConnLock); -#endif - - oldCancelConn = cancelConn; - /* be sure handle_sigint doesn't use pointer while freeing */ - cancelConn = NULL; - - if (oldCancelConn != NULL) - PQfreeCancel(oldCancelConn); - -#ifdef WIN32 - LeaveCriticalSection(&cancelConnLock); -#endif -} - /* * AcceptResult @@ -707,7 +573,7 @@ PSQLexec(const char *query) return NULL; } - SetCancelConn(); + SetCancelConn(pset.db); res = PQexec(pset.db, query); @@ -746,7 +612,7 @@ PSQLexecWatch(const char *query, const printQueryOpt *opt) return 0; } - SetCancelConn(); + SetCancelConn(pset.db); if (pset.timing) INSTR_TIME_SET_CURRENT(before); @@ -773,7 +639,7 @@ PSQLexecWatch(const char *query, const printQueryOpt *opt) * consumed. The user's intention, though, is to cancel the entire watch * process, so detect a sent cancellation request and exit in this case. */ - if (cancel_pressed) + if (CancelRequested) { PQclear(res); return 0; @@ -973,8 +839,8 @@ ExecQueryTuples(const PGresult *result) { const char *query = PQgetvalue(result, r, c); - /* Abandon execution if cancel_pressed */ - if (cancel_pressed) + /* Abandon execution if CancelRequested */ + if (CancelRequested) goto loop_exit; /* @@ -1091,7 +957,7 @@ ProcessResult(PGresult **results) FILE *copystream; PGresult *copy_result; - SetCancelConn(); + SetCancelConn(pset.db); if (result_status == PGRES_COPY_OUT) { bool need_close = false; @@ -1342,7 +1208,7 @@ SendQuery(const char *query) if (fgets(buf, sizeof(buf), stdin) != NULL) if (buf[0] == 'x') goto sendquery_cleanup; - if (cancel_pressed) + if (CancelRequested) goto sendquery_cleanup; } else if (pset.echo == PSQL_ECHO_QUERIES) @@ -1360,7 +1226,7 @@ SendQuery(const char *query) fflush(pset.logfile); } - SetCancelConn(); + SetCancelConn(pset.db); transaction_status = PQtransactionStatus(pset.db); @@ -1886,7 +1752,7 @@ ExecQueryUsingCursor(const char *query, double *elapsed_msec) * writing things to the stream, we presume $PAGER has disappeared and * stop bothering to pull down more data. */ - if (ntuples < fetch_count || cancel_pressed || flush_error || + if (ntuples < fetch_count || CancelRequested || flush_error || ferror(fout)) break; } diff --git a/src/bin/psql/common.h b/src/bin/psql/common.h index 282a520116..0b87ec5836 100644 --- a/src/bin/psql/common.h +++ b/src/bin/psql/common.h @@ -26,10 +26,7 @@ extern volatile bool sigint_interrupt_enabled; extern sigjmp_buf sigint_interrupt_jmp; -extern void setup_cancel_handler(void); - -extern void SetCancelConn(void); -extern void ResetCancelConn(void); +extern void psql_setup_cancel_handler(void); extern PGresult *PSQLexec(const char *query); extern int PSQLexecWatch(const char *query, const printQueryOpt *opt); diff --git a/src/bin/psql/large_obj.c b/src/bin/psql/large_obj.c index 042403e0f7..b2ddb3512e 100644 --- a/src/bin/psql/large_obj.c +++ b/src/bin/psql/large_obj.c @@ -11,6 +11,7 @@ #include "common/logging.h" #include "large_obj.h" #include "settings.h" +#include "fe_utils/cancel.h" static void print_lo_result(const char *fmt,...) pg_attribute_printf(1, 2); @@ -146,7 +147,7 @@ do_lo_export(const char *loid_arg, const char *filename_arg) if (!start_lo_xact("\\lo_export", &own_transaction)) return false; - SetCancelConn(); + SetCancelConn(NULL); status = lo_export(pset.db, atooid(loid_arg), filename_arg); ResetCancelConn(); @@ -182,7 +183,7 @@ do_lo_import(const char *filename_arg, const char *comment_arg) if (!start_lo_xact("\\lo_import", &own_transaction)) return false; - SetCancelConn(); + SetCancelConn(NULL); loid = lo_import(pset.db, filename_arg); ResetCancelConn(); @@ -244,7 +245,7 @@ do_lo_unlink(const char *loid_arg) if (!start_lo_xact("\\lo_unlink", &own_transaction)) return false; - SetCancelConn(); + SetCancelConn(NULL); status = lo_unlink(pset.db, loid); ResetCancelConn(); diff --git a/src/bin/psql/startup.c b/src/bin/psql/startup.c index 0d941ef5ba..43cf139a31 100644 --- a/src/bin/psql/startup.c +++ b/src/bin/psql/startup.c @@ -301,7 +301,7 @@ main(int argc, char *argv[]) exit(EXIT_BADCONN); } - setup_cancel_handler(); + psql_setup_cancel_handler(); PQsetNoticeProcessor(pset.db, NoticeProcessor, NULL); diff --git a/src/bin/scripts/clusterdb.c b/src/bin/scripts/clusterdb.c index d380127356..3aee5f2834 100644 --- a/src/bin/scripts/clusterdb.c +++ b/src/bin/scripts/clusterdb.c @@ -133,7 +133,7 @@ main(int argc, char *argv[]) exit(1); } - setup_cancel_handler(); + setup_cancel_handler(NULL); if (alldb) { diff --git a/src/bin/scripts/common.c b/src/bin/scripts/common.c index 1b38a1da49..d2a7547441 100644 --- a/src/bin/scripts/common.c +++ b/src/bin/scripts/common.c @@ -24,14 +24,6 @@ #define ERRCODE_UNDEFINED_TABLE "42P01" - -static PGcancel *volatile cancelConn = NULL; -bool CancelRequested = false; - -#ifdef WIN32 -static CRITICAL_SECTION cancelConnLock; -#endif - /* * Provide strictly harmonized handling of --help and --version * options. @@ -465,142 +457,3 @@ yesno_prompt(const char *question) _(PG_YESLETTER), _(PG_NOLETTER)); } } - -/* - * SetCancelConn - * - * Set cancelConn to point to the current database connection. - */ -void -SetCancelConn(PGconn *conn) -{ - PGcancel *oldCancelConn; - -#ifdef WIN32 - EnterCriticalSection(&cancelConnLock); -#endif - - /* Free the old one if we have one */ - oldCancelConn = cancelConn; - - /* be sure handle_sigint doesn't use pointer while freeing */ - cancelConn = NULL; - - if (oldCancelConn != NULL) - PQfreeCancel(oldCancelConn); - - cancelConn = PQgetCancel(conn); - -#ifdef WIN32 - LeaveCriticalSection(&cancelConnLock); -#endif -} - -/* - * ResetCancelConn - * - * Free the current cancel connection, if any, and set to NULL. - */ -void -ResetCancelConn(void) -{ - PGcancel *oldCancelConn; - -#ifdef WIN32 - EnterCriticalSection(&cancelConnLock); -#endif - - oldCancelConn = cancelConn; - - /* be sure handle_sigint doesn't use pointer while freeing */ - cancelConn = NULL; - - if (oldCancelConn != NULL) - PQfreeCancel(oldCancelConn); - -#ifdef WIN32 - LeaveCriticalSection(&cancelConnLock); -#endif -} - -#ifndef WIN32 -/* - * Handle interrupt signals by canceling the current command, if a cancelConn - * is set. - */ -static void -handle_sigint(SIGNAL_ARGS) -{ - int save_errno = errno; - char errbuf[256]; - - /* Send QueryCancel if we are processing a database query */ - if (cancelConn != NULL) - { - if (PQcancel(cancelConn, errbuf, sizeof(errbuf))) - { - CancelRequested = true; - fprintf(stderr, _("Cancel request sent\n")); - } - else - fprintf(stderr, _("Could not send cancel request: %s"), errbuf); - } - else - CancelRequested = true; - - errno = save_errno; /* just in case the write changed it */ -} - -void -setup_cancel_handler(void) -{ - pqsignal(SIGINT, handle_sigint); -} -#else /* WIN32 */ - -/* - * Console control handler for Win32. Note that the control handler will - * execute on a *different thread* than the main one, so we need to do - * proper locking around those structures. - */ -static BOOL WINAPI -consoleHandler(DWORD dwCtrlType) -{ - char errbuf[256]; - - if (dwCtrlType == CTRL_C_EVENT || - dwCtrlType == CTRL_BREAK_EVENT) - { - /* Send QueryCancel if we are processing a database query */ - EnterCriticalSection(&cancelConnLock); - if (cancelConn != NULL) - { - if (PQcancel(cancelConn, errbuf, sizeof(errbuf))) - { - fprintf(stderr, _("Cancel request sent\n")); - CancelRequested = true; - } - else - fprintf(stderr, _("Could not send cancel request: %s"), errbuf); - } - else - CancelRequested = true; - - LeaveCriticalSection(&cancelConnLock); - - return TRUE; - } - else - /* Return FALSE for any signals not being handled */ - return FALSE; -} - -void -setup_cancel_handler(void) -{ - InitializeCriticalSection(&cancelConnLock); - - SetConsoleCtrlHandler(consoleHandler, TRUE); -} - -#endif /* WIN32 */ diff --git a/src/bin/scripts/common.h b/src/bin/scripts/common.h index f36b26a576..12748258a6 100644 --- a/src/bin/scripts/common.h +++ b/src/bin/scripts/common.h @@ -13,6 +13,7 @@ #include "libpq-fe.h" #include "getopt_long.h" /* pgrminclude ignore */ #include "pqexpbuffer.h" /* pgrminclude ignore */ +#include "fe_utils/cancel.h" enum trivalue { @@ -60,10 +61,4 @@ extern void appendQualifiedRelation(PQExpBuffer buf, const char *name, extern bool yesno_prompt(const char *question); -extern void setup_cancel_handler(void); - -extern void SetCancelConn(PGconn *conn); -extern void ResetCancelConn(void); - - #endif /* COMMON_H */ diff --git a/src/bin/scripts/reindexdb.c b/src/bin/scripts/reindexdb.c index f00aec15de..bedd95cf9d 100644 --- a/src/bin/scripts/reindexdb.c +++ b/src/bin/scripts/reindexdb.c @@ -187,7 +187,7 @@ main(int argc, char *argv[]) exit(1); } - setup_cancel_handler(); + setup_cancel_handler(NULL); if (alldb) { diff --git a/src/bin/scripts/vacuumdb.c b/src/bin/scripts/vacuumdb.c index 2c7219239f..83a94dc632 100644 --- a/src/bin/scripts/vacuumdb.c +++ b/src/bin/scripts/vacuumdb.c @@ -257,7 +257,7 @@ main(int argc, char *argv[]) /* allow 'and_analyze' with 'analyze_only' */ } - setup_cancel_handler(); + setup_cancel_handler(NULL); /* Avoid opening extra connections. */ if (tbl_count && (concurrentCons > tbl_count)) diff --git a/src/fe_utils/Makefile b/src/fe_utils/Makefile index f2e516a2aa..43a5bf5d49 100644 --- a/src/fe_utils/Makefile +++ b/src/fe_utils/Makefile @@ -20,7 +20,7 @@ include $(top_builddir)/src/Makefile.global override CPPFLAGS := -DFRONTEND -I$(libpq_srcdir) $(CPPFLAGS) OBJS = conditional.o mbprint.o print.o psqlscan.o recovery_gen.o \ - simple_list.o string_utils.o + simple_list.o string_utils.o cancel.o all: libpgfeutils.a diff --git a/src/fe_utils/cancel.c b/src/fe_utils/cancel.c new file mode 100644 index 0000000000..f8a8299fcb --- /dev/null +++ b/src/fe_utils/cancel.c @@ -0,0 +1,194 @@ +/* + * interrupt a connection + * + * Copyright (c) 2000-2019, PostgreSQL Global Development Group + * + * src/fe-utils/cancel.c + */ + +#include "postgres_fe.h" + +#include <signal.h> +#include <unistd.h> + +#include "libpq-fe.h" +#include "fe_utils/connect.h" +#include "fe_utils/string_utils.h" +#include "fe_utils/cancel.h" + +/* + * Write a simple string to stderr --- must be safe in a signal handler. + * We ignore the write() result since there's not much we could do about it. + * Certain compilers make that harder than it ought to be. + */ +#define write_stderr(str) \ + do { \ + const char *str_ = (str); \ + int rc_; \ + rc_ = write(fileno(stderr), str_, strlen(str_)); \ + (void) rc_; \ + } while (0) + +static PGcancel *volatile cancelConn = NULL; +bool CancelRequested = false; + +#ifdef WIN32 +static CRITICAL_SECTION cancelConnLock; +#endif + +/* + * SetCancelConn + * + * Set cancelConn to point to the current database connection. + */ +void +SetCancelConn(PGconn *conn) +{ + PGcancel *oldCancelConn; + +#ifdef WIN32 + EnterCriticalSection(&cancelConnLock); +#endif + + /* Free the old one if we have one */ + oldCancelConn = cancelConn; + /* be sure handle_sigint doesn't use pointer while freeing */ + cancelConn = NULL; + + if (oldCancelConn != NULL) + PQfreeCancel(oldCancelConn); + + cancelConn = PQgetCancel(conn); + +#ifdef WIN32 + LeaveCriticalSection(&cancelConnLock); +#endif +} + + +/* + * ResetCancelConn + * + * Free the current cancel connection, if any, and set to NULL. + */ +void +ResetCancelConn(void) +{ + PGcancel *oldCancelConn; + +#ifdef WIN32 + EnterCriticalSection(&cancelConnLock); +#endif + + oldCancelConn = cancelConn; + /* be sure handle_sigint doesn't use pointer while freeing */ + cancelConn = NULL; + + if (oldCancelConn != NULL) + PQfreeCancel(oldCancelConn); + +#ifdef WIN32 + LeaveCriticalSection(&cancelConnLock); +#endif +} + +#ifndef WIN32 +/* + * Additional callback to handle interrupts + */ +static void (*sigint_callback)(void) = NULL; + +/* + * Handle interrupt signals by canceling the current command, if a cancelConn + * is set. + */ +static void +handle_sigint(SIGNAL_ARGS) +{ + int save_errno = errno; + char errbuf[256]; + + if (sigint_callback != NULL) + sigint_callback(); + + /* Send QueryCancel if we are processing a database query */ + if (cancelConn != NULL) + { + if (PQcancel(cancelConn, errbuf, sizeof(errbuf))) + { + CancelRequested = true; + write_stderr(_("Cancel request sent\n")); + } + else + { + write_stderr(_("Could not send cancel request: ")); + write_stderr(errbuf); + } + } + else + CancelRequested = true; + + errno = save_errno; /* just in case the write changed it */ +} + +void +setup_cancel_handler(void (*callback)(void)) +{ + sigint_callback = callback; + pqsignal(SIGINT, handle_sigint); + /* what about SIGTERM? SIGQUIT? */ +} + +#else /* WIN32 */ + +/* + * Console control handler for Win32. Note that the control handler will + * execute on a *different thread* than the main one, so we need to do + * proper locking around those structures. + */ +static BOOL WINAPI +consoleHandler(DWORD dwCtrlType) +{ + char errbuf[256]; + + if (dwCtrlType == CTRL_C_EVENT || + dwCtrlType == CTRL_BREAK_EVENT) + { + /* Send QueryCancel if we are processing a database query */ + EnterCriticalSection(&cancelConnLock); + if (cancelConn != NULL) + { + if (PQcancel(cancelConn, errbuf, sizeof(errbuf))) + { + write_stderr(_("Cancel request sent\n")); + CancelRequested = true; + } + else + { + write_stderr(_("Could not send cancel request: %s")); + write_stderr(errbuf); + } + } + else + CancelRequested = true; + + LeaveCriticalSection(&cancelConnLock); + + return TRUE; + } + else + /* Return FALSE for any signals not being handled */ + return FALSE; +} + +void +setup_cancel_handler(void *ignored) +{ + Assert(ignored == NULL); + + InitializeCriticalSection(&cancelConnLock); + + SetConsoleCtrlHandler(consoleHandler, TRUE); +} + +#endif /* WIN32 */ diff --git a/src/include/fe_utils/cancel.h b/src/include/fe_utils/cancel.h new file mode 100644 index 0000000000..686d7c9dc4 --- /dev/null +++ b/src/include/fe_utils/cancel.h @@ -0,0 +1,30 @@ +/*------------------------------------------------------------------------- + * + * Query Cancelation + * + * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/include/fe_utils/cancel.h + * + *------------------------------------------------------------------------- + */ + +#ifndef CANCEL_H +#define CANCEL_H + +extern void SetCancelConn(PGconn *conn); +extern void ResetCancelConn(void); +extern bool CancelRequested; + +#ifndef WIN32 +extern void setup_cancel_handler(void (*callback)(void)); +#else +/* + * Ensure that the signature is the same under windows, at the price of + * an ignored function parameter. + */ +extern void setup_cancel_handler(void *ignored); +#endif /* WIN32 */ + +#endif /* CANCEL_H */ diff --git a/src/tools/msvc/Mkvcbuild.pm b/src/tools/msvc/Mkvcbuild.pm index 7a103e6140..e6e04a7ea6 100644 --- a/src/tools/msvc/Mkvcbuild.pm +++ b/src/tools/msvc/Mkvcbuild.pm @@ -143,7 +143,7 @@ sub mkvcbuild our @pgfeutilsfiles = qw( conditional.c mbprint.c print.c psqlscan.l psqlscan.c - simple_list.c string_utils.c recovery_gen.c); + simple_list.c string_utils.c recovery_gen.c cancel.c); $libpgport = $solution->AddProject('libpgport', 'lib', 'misc'); $libpgport->AddDefine('FRONTEND');