Re: Proof of concept: standalone backend with full FE/BE protocol - Mailing list pgsql-hackers
From | Tom Lane |
---|---|
Subject | Re: Proof of concept: standalone backend with full FE/BE protocol |
Date | |
Msg-id | 18870.1347040148@sss.pgh.pa.us Whole thread Raw |
In response to | Re: Proof of concept: standalone backend with full FE/BE protocol (Heikki Linnakangas <hlinnaka@iki.fi>) |
Responses |
Re: Proof of concept: standalone backend with full FE/BE
protocol
Re: Proof of concept: standalone backend with full FE/BE protocol |
List | pgsql-hackers |
Heikki Linnakangas <hlinnaka@iki.fi> writes: > Would socketpair(2) be simpler? Attached is a revised version of the patch that uses socketpair(2). This is definitely a lot less invasive --- the backend side of the patch, in particular, is far shorter, and there are fewer portability hazards since we're not trying to replace sockets with pipes. I've not done anything yet about the potential security issues associated with untrusted libpq connection strings. I think this is still at the proof-of-concept stage; in particular, it's probably time to see if we can make it work on Windows before we worry more about that. I'm a bit tempted though to pull out and apply the portions of the patch that replace libpq's assorted ad-hoc closesocket() calls with a centralized pqDropConnection routine. I think that's probably a good idea independently of this feature. regards, tom lane diff --git a/src/backend/main/main.c b/src/backend/main/main.c index 33c5a0a4e645624515016397da0011423d993c70..968959b85ef53aa2ec0ef8c5c5a9bc544291bfe5 100644 *** a/src/backend/main/main.c --- b/src/backend/main/main.c *************** main(int argc, char *argv[]) *** 191,196 **** --- 191,198 ---- AuxiliaryProcessMain(argc, argv); /* does not return */ else if (argc > 1 && strcmp(argv[1], "--describe-config") == 0) GucInfoMain(); /* does not return */ + else if (argc > 1 && strncmp(argv[1], "--child=", 8) == 0) + ChildPostgresMain(argc, argv, get_current_username(progname)); /* does not return */ else if (argc > 1 && strcmp(argv[1], "--single") == 0) PostgresMain(argc, argv, get_current_username(progname)); /* does not return */ else diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c index 73520a6ca2f4d3300f3d8939c0e9412064a911c3..5a51fa9cb9abb39b0ac02d21a25e5940159582ea 100644 *** a/src/backend/postmaster/postmaster.c --- b/src/backend/postmaster/postmaster.c *************** ExitPostmaster(int status) *** 4268,4273 **** --- 4268,4350 ---- proc_exit(status); } + + /* + * ChildPostgresMain - start a new-style standalone postgres process + * + * This may not belong here, but it does share a lot of code with ConnCreate + * and BackendInitialize. Basically what it has to do is set up a + * MyProcPort structure and then hand off control to PostgresMain. + * Beware that not very much stuff is initialized yet. + * + * In the future it might be interesting to support a "standalone + * multiprocess" mode in which we have a postmaster process that doesn't + * listen for connections, but does supervise autovacuum, bgwriter, etc + * auxiliary processes. So that's another reason why postmaster.c might be + * the right place for this. + */ + void + ChildPostgresMain(int argc, char *argv[], const char *username) + { + Port *port; + + /* + * Fire up essential subsystems: error and memory management + */ + MemoryContextInit(); + + /* + * Build a Port structure for the client connection + */ + if (!(port = (Port *) calloc(1, sizeof(Port)))) + ereport(FATAL, + (errcode(ERRCODE_OUT_OF_MEMORY), + errmsg("out of memory"))); + + /* + * GSSAPI specific state struct must exist even though we won't use it + */ + #if defined(ENABLE_GSS) || defined(ENABLE_SSPI) + port->gss = (pg_gssinfo *) calloc(1, sizeof(pg_gssinfo)); + if (!port->gss) + ereport(FATAL, + (errcode(ERRCODE_OUT_OF_MEMORY), + errmsg("out of memory"))); + #endif + + /* The file descriptor of the client socket is the argument of --child */ + if (sscanf(argv[1], "--child=%d", &port->sock) != 1) + ereport(FATAL, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("invalid argument for --child: \"%s\"", argv[1]))); + + /* Default assumption about protocol to use */ + FrontendProtocol = port->proto = PG_PROTOCOL_LATEST; + + /* save process start time */ + port->SessionStartTime = GetCurrentTimestamp(); + MyStartTime = timestamptz_to_time_t(port->SessionStartTime); + + /* set these to empty in case they are needed */ + port->remote_host = ""; + port->remote_port = ""; + + MyProcPort = port; + + /* + * We can now initialize libpq and then enable reporting of ereport errors + * to the client. + */ + pq_init(); /* initialize libpq to talk to client */ + whereToSendOutput = DestRemote; /* now safe to ereport to client */ + + /* And pass off control to PostgresMain */ + PostgresMain(argc, argv, username); + + abort(); /* not reached */ + } + + /* * sigusr1_handler - handle signal conditions from child processes */ diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c index f1248a851bf90188da8d3a7e8b61ac99bf78ebbd..c6a7de63c77f79c85be2170d4e5254b790f3fd5e 100644 *** a/src/backend/tcop/postgres.c --- b/src/backend/tcop/postgres.c *************** process_postgres_switches(int argc, char *** 3257,3264 **** { gucsource = PGC_S_ARGV; /* switches came from command line */ ! /* Ignore the initial --single argument, if present */ ! if (argc > 1 && strcmp(argv[1], "--single") == 0) { argv++; argc--; --- 3257,3266 ---- { gucsource = PGC_S_ARGV; /* switches came from command line */ ! /* Ignore the initial --single or --child argument, if present */ ! if (argc > 1 && ! (strcmp(argv[1], "--single") == 0 || ! strncmp(argv[1], "--child=", 8) == 0)) { argv++; argc--; *************** PostgresMain(int argc, char *argv[], con *** 3522,3539 **** * standalone). */ if (!IsUnderPostmaster) - { MyProcPid = getpid(); MyStartTime = time(NULL); - } /* * Fire up essential subsystems: error and memory management * ! * If we are running under the postmaster, this is done already. */ ! if (!IsUnderPostmaster) MemoryContextInit(); SetProcessingMode(InitProcessing); --- 3524,3541 ---- * standalone). */ if (!IsUnderPostmaster) MyProcPid = getpid(); + if (MyProcPort == NULL) MyStartTime = time(NULL); /* * Fire up essential subsystems: error and memory management * ! * If we are running under the postmaster, this is done already; likewise ! * in child-postgres mode. */ ! if (MyProcPort == NULL) MemoryContextInit(); SetProcessingMode(InitProcessing); diff --git a/src/include/postmaster/postmaster.h b/src/include/postmaster/postmaster.h index 0fe7ec26db7646472fca248dee6982dc5c595c6c..6d4245cdc05ea5fea4baccf5dbc52dade7c00ff0 100644 *** a/src/include/postmaster/postmaster.h --- b/src/include/postmaster/postmaster.h *************** extern int postmaster_alive_fds[2]; *** 47,52 **** --- 47,55 ---- extern const char *progname; extern void PostmasterMain(int argc, char *argv[]) __attribute__((noreturn)); + + extern void ChildPostgresMain(int argc, char *argv[], const char *username) __attribute__((noreturn)); + extern void ClosePostmasterPorts(bool am_syslogger); extern int MaxLivePostmasterChildren(void); diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c index 3dcd0c3f9c09ec06144c040cc333e382b9d7f78b..c578a8ebbe6874825afbd0cb27cdfa2a3d01aec7 100644 *** a/src/interfaces/libpq/fe-connect.c --- b/src/interfaces/libpq/fe-connect.c *************** *** 17,22 **** --- 17,23 ---- #include <sys/types.h> #include <sys/stat.h> + #include <sys/wait.h> #include <fcntl.h> #include <ctype.h> #include <time.h> *************** static const PQconninfoOption PQconninfo *** 259,264 **** --- 260,271 ---- {"replication", NULL, NULL, NULL, "Replication", "D", 5}, + {"standalone_datadir", NULL, NULL, NULL, + "Standalone-Data-Directory", "D", 64}, + + {"standalone_backend", NULL, NULL, NULL, + "Standalone-Backend", "D", 64}, + /* Terminating entry --- MUST BE LAST */ {NULL, NULL, NULL, NULL, NULL, NULL, 0} *************** pgthreadlock_t pg_g_threadlock = default *** 345,350 **** --- 352,390 ---- /* + * pqDropConnection + * + * Close any physical connection to the server, and reset associated + * state inside the connection object. We don't release state that + * would be needed to reconnect, though. + */ + void + pqDropConnection(PGconn *conn) + { + /* Drop any SSL state */ + pqsecure_close(conn); + /* Close the socket itself */ + if (conn->sock >= 0) + closesocket(conn->sock); + conn->sock = -1; + /* If we forked a child postgres process, wait for it to exit */ + if (conn->postgres_pid > 0) + { + while (waitpid(conn->postgres_pid, NULL, 0) < 0) + { + /* If interrupted by signal, keep waiting */ + if (errno != EINTR) + break; + } + } + conn->postgres_pid = -1; + /* Discard any unread/unsent data */ + conn->inStart = conn->inCursor = conn->inEnd = 0; + conn->outCount = 0; + } + + + /* * Connecting to a Database * * There are now six different ways a user of this API can connect to the *************** fillPGconn(PGconn *conn, PQconninfoOptio *** 621,626 **** --- 661,670 ---- conn->pghost = tmp ? strdup(tmp) : NULL; tmp = conninfo_getval(connOptions, "port"); conn->pgport = tmp ? strdup(tmp) : NULL; + tmp = conninfo_getval(connOptions, "standalone_datadir"); + conn->standalone_datadir = tmp ? strdup(tmp) : NULL; + tmp = conninfo_getval(connOptions, "standalone_backend"); + conn->standalone_backend = tmp ? strdup(tmp) : NULL; tmp = conninfo_getval(connOptions, "tty"); conn->pgtty = tmp ? strdup(tmp) : NULL; tmp = conninfo_getval(connOptions, "options"); *************** setKeepalivesWin32(PGconn *conn) *** 1289,1294 **** --- 1333,1384 ---- #endif /* SIO_KEEPALIVE_VALS */ #endif /* WIN32 */ + /* + * Fork and exec a command, connecting up a pair of anonymous sockets for + * communication. argv[fdarg] is modified by appending the child-side + * socket's file descriptor number. The parent-side socket's FD is returned + * in *psock, and the function result is the child's PID. + */ + static pid_t + fork_backend_child(char **argv, int fdarg, int *psock) + { + pid_t pid; + int socks[2]; + char newfdarg[32]; + + if (socketpair(AF_UNIX, SOCK_STREAM, 0, socks) < 0) + return -1; + + pid = fork(); + + if (pid < 0) + { + /* fork failed */ + close(socks[0]); + close(socks[1]); + return pid; + } + else if (pid == 0) + { + /* successful, in child process */ + close(socks[1]); + snprintf(newfdarg, sizeof(newfdarg), "%s%d", argv[fdarg], socks[0]); + argv[fdarg] = newfdarg; + execv(argv[0], argv); + perror(argv[0]); + exit(1); + } + else + { + /* successful, in parent process */ + close(socks[0]); + *psock = socks[1]; + } + + return pid; + } + + /* ---------- * connectDBStart - * Begin the process of making a connection to the backend. *************** connectDBStart(PGconn *conn) *** 1317,1322 **** --- 1407,1461 ---- conn->outCount = 0; /* + * If the standalone_datadir option was specified, ignore any host or + * port specifications and just try to fork a standalone backend. + */ + if (conn->standalone_datadir && conn->standalone_datadir[0]) + { + char *be_argv[10]; + + /* + * We set up the backend's command line in execv(3) style, so that + * we don't need to cope with shell quoting rules. + */ + if (conn->standalone_backend && conn->standalone_backend[0]) + be_argv[0] = conn->standalone_backend; + else /* assume we should use hard-wired path */ + be_argv[0] = PGBINDIR "/postgres"; + + be_argv[1] = "--child="; + be_argv[2] = "-D"; + be_argv[3] = conn->standalone_datadir; + be_argv[4] = (conn->dbName && conn->dbName[0]) ? conn->dbName : NULL; + be_argv[5] = NULL; + + conn->postgres_pid = fork_backend_child(be_argv, 1, &conn->sock); + if (conn->postgres_pid < 0) + { + char sebuf[256]; + + appendPQExpBuffer(&conn->errorMessage, + libpq_gettext("could not fork standalone backend: %s\n"), + pqStrerror(errno, sebuf, sizeof(sebuf))); + goto connect_errReturn; + } + + /* + * Go directly to CONNECTION_AUTH_OK state, since the standalone + * backend is not going to issue any authentication challenge to us. + * We're just waiting for startup to conclude. + */ + #ifdef USE_SSL + conn->allow_ssl_try = false; + #endif + conn->pversion = PG_PROTOCOL(3, 0); + conn->status = CONNECTION_AUTH_OK; + conn->asyncStatus = PGASYNC_BUSY; + + return 1; + } + + /* * Determine the parameters to pass to pg_getaddrinfo_all. */ *************** connectDBStart(PGconn *conn) *** 1416,1427 **** return 1; connect_errReturn: ! if (conn->sock >= 0) ! { ! pqsecure_close(conn); ! closesocket(conn->sock); ! conn->sock = -1; ! } conn->status = CONNECTION_BAD; return 0; } --- 1555,1561 ---- return 1; connect_errReturn: ! pqDropConnection(conn); conn->status = CONNECTION_BAD; return 0; } *************** keep_going: /* We will come back to *** 1644,1651 **** { if (!connectNoDelay(conn)) { ! closesocket(conn->sock); ! conn->sock = -1; conn->addr_cur = addr_cur->ai_next; continue; } --- 1778,1784 ---- { if (!connectNoDelay(conn)) { ! pqDropConnection(conn); conn->addr_cur = addr_cur->ai_next; continue; } *************** keep_going: /* We will come back to *** 1655,1662 **** appendPQExpBuffer(&conn->errorMessage, libpq_gettext("could not set socket to non-blocking mode: %s\n"), SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf))); ! closesocket(conn->sock); ! conn->sock = -1; conn->addr_cur = addr_cur->ai_next; continue; } --- 1788,1794 ---- appendPQExpBuffer(&conn->errorMessage, libpq_gettext("could not set socket to non-blocking mode: %s\n"), SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf))); ! pqDropConnection(conn); conn->addr_cur = addr_cur->ai_next; continue; } *************** keep_going: /* We will come back to *** 1667,1674 **** appendPQExpBuffer(&conn->errorMessage, libpq_gettext("could not set socket to close-on-exec mode: %s\n"), SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf))); ! closesocket(conn->sock); ! conn->sock = -1; conn->addr_cur = addr_cur->ai_next; continue; } --- 1799,1805 ---- appendPQExpBuffer(&conn->errorMessage, libpq_gettext("could not set socket to close-on-exec mode: %s\n"), SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf))); ! pqDropConnection(conn); conn->addr_cur = addr_cur->ai_next; continue; } *************** keep_going: /* We will come back to *** 1715,1722 **** if (err) { ! closesocket(conn->sock); ! conn->sock = -1; conn->addr_cur = addr_cur->ai_next; continue; } --- 1846,1852 ---- if (err) { ! pqDropConnection(conn); conn->addr_cur = addr_cur->ai_next; continue; } *************** keep_going: /* We will come back to *** 1802,1812 **** * failure and keep going if there are more addresses. */ connectFailureMessage(conn, SOCK_ERRNO); ! if (conn->sock >= 0) ! { ! closesocket(conn->sock); ! conn->sock = -1; ! } /* * Try the next address, if any. --- 1932,1938 ---- * failure and keep going if there are more addresses. */ connectFailureMessage(conn, SOCK_ERRNO); ! pqDropConnection(conn); /* * Try the next address, if any. *************** keep_going: /* We will come back to *** 1851,1856 **** --- 1977,1983 ---- * error message. */ connectFailureMessage(conn, optval); + pqDropConnection(conn); /* * If more addresses remain, keep trying, just as in the *************** keep_going: /* We will come back to *** 1858,1868 **** */ if (conn->addr_cur->ai_next != NULL) { - if (conn->sock >= 0) - { - closesocket(conn->sock); - conn->sock = -1; - } conn->addr_cur = conn->addr_cur->ai_next; conn->status = CONNECTION_NEEDED; goto keep_going; --- 1985,1990 ---- *************** keep_going: /* We will come back to *** 2137,2148 **** /* only retry once */ conn->allow_ssl_try = false; /* Must drop the old connection */ ! closesocket(conn->sock); ! conn->sock = -1; conn->status = CONNECTION_NEEDED; - /* Discard any unread/unsent data */ - conn->inStart = conn->inCursor = conn->inEnd = 0; - conn->outCount = 0; goto keep_going; } } --- 2259,2266 ---- /* only retry once */ conn->allow_ssl_try = false; /* Must drop the old connection */ ! pqDropConnection(conn); conn->status = CONNECTION_NEEDED; goto keep_going; } } *************** keep_going: /* We will come back to *** 2252,2264 **** { conn->pversion = PG_PROTOCOL(2, 0); /* Must drop the old connection */ ! pqsecure_close(conn); ! closesocket(conn->sock); ! conn->sock = -1; conn->status = CONNECTION_NEEDED; - /* Discard any unread/unsent data */ - conn->inStart = conn->inCursor = conn->inEnd = 0; - conn->outCount = 0; goto keep_going; } --- 2370,2377 ---- { conn->pversion = PG_PROTOCOL(2, 0); /* Must drop the old connection */ ! pqDropConnection(conn); conn->status = CONNECTION_NEEDED; goto keep_going; } *************** keep_going: /* We will come back to *** 2323,2334 **** /* only retry once */ conn->wait_ssl_try = false; /* Must drop the old connection */ ! closesocket(conn->sock); ! conn->sock = -1; conn->status = CONNECTION_NEEDED; - /* Discard any unread/unsent data */ - conn->inStart = conn->inCursor = conn->inEnd = 0; - conn->outCount = 0; goto keep_going; } --- 2436,2443 ---- /* only retry once */ conn->wait_ssl_try = false; /* Must drop the old connection */ ! pqDropConnection(conn); conn->status = CONNECTION_NEEDED; goto keep_going; } *************** keep_going: /* We will come back to *** 2343,2355 **** /* only retry once */ conn->allow_ssl_try = false; /* Must drop the old connection */ ! pqsecure_close(conn); ! closesocket(conn->sock); ! conn->sock = -1; conn->status = CONNECTION_NEEDED; - /* Discard any unread/unsent data */ - conn->inStart = conn->inCursor = conn->inEnd = 0; - conn->outCount = 0; goto keep_going; } #endif --- 2452,2459 ---- /* only retry once */ conn->allow_ssl_try = false; /* Must drop the old connection */ ! pqDropConnection(conn); conn->status = CONNECTION_NEEDED; goto keep_going; } #endif *************** keep_going: /* We will come back to *** 2509,2521 **** PQclear(res); conn->send_appname = false; /* Must drop the old connection */ ! pqsecure_close(conn); ! closesocket(conn->sock); ! conn->sock = -1; conn->status = CONNECTION_NEEDED; - /* Discard any unread/unsent data */ - conn->inStart = conn->inCursor = conn->inEnd = 0; - conn->outCount = 0; goto keep_going; } } --- 2613,2620 ---- PQclear(res); conn->send_appname = false; /* Must drop the old connection */ ! pqDropConnection(conn); conn->status = CONNECTION_NEEDED; goto keep_going; } } *************** makeEmptyPGconn(void) *** 2726,2731 **** --- 2825,2831 ---- conn->auth_req_received = false; conn->password_needed = false; conn->dot_pgpass_used = false; + conn->postgres_pid = -1; #ifdef USE_SSL conn->allow_ssl_try = true; conn->wait_ssl_try = false; *************** freePGconn(PGconn *conn) *** 2802,2807 **** --- 2902,2911 ---- free(conn->pgport); if (conn->pgunixsocket) free(conn->pgunixsocket); + if (conn->standalone_datadir) + free(conn->standalone_datadir); + if (conn->standalone_backend) + free(conn->standalone_backend); if (conn->pgtty) free(conn->pgtty); if (conn->connect_timeout) *************** closePGconn(PGconn *conn) *** 2909,2920 **** /* * Close the connection, reset all transient state, flush I/O buffers. */ ! if (conn->sock >= 0) ! { ! pqsecure_close(conn); ! closesocket(conn->sock); ! } ! conn->sock = -1; conn->status = CONNECTION_BAD; /* Well, not really _bad_ - just * absent */ conn->asyncStatus = PGASYNC_IDLE; --- 3013,3019 ---- /* * Close the connection, reset all transient state, flush I/O buffers. */ ! pqDropConnection(conn); conn->status = CONNECTION_BAD; /* Well, not really _bad_ - just * absent */ conn->asyncStatus = PGASYNC_IDLE; *************** closePGconn(PGconn *conn) *** 2943,2950 **** if (conn->lobjfuncs) free(conn->lobjfuncs); conn->lobjfuncs = NULL; - conn->inStart = conn->inCursor = conn->inEnd = 0; - conn->outCount = 0; #ifdef ENABLE_GSS { OM_uint32 min_s; --- 3042,3047 ---- diff --git a/src/interfaces/libpq/fe-misc.c b/src/interfaces/libpq/fe-misc.c index b5e5519c416aa767921a5549f7947f80e4cf8250..0cd0d4288f1c3a859eebc5708b6a44d7e1c3da52 100644 *** a/src/interfaces/libpq/fe-misc.c --- b/src/interfaces/libpq/fe-misc.c *************** retry4: *** 780,790 **** * has been set already. */ definitelyFailed: conn->status = CONNECTION_BAD; /* No more connection to backend */ - pqsecure_close(conn); - closesocket(conn->sock); - conn->sock = -1; - return -1; } --- 780,787 ---- * has been set already. */ definitelyFailed: + pqDropConnection(conn); conn->status = CONNECTION_BAD; /* No more connection to backend */ return -1; } diff --git a/src/interfaces/libpq/fe-protocol3.c b/src/interfaces/libpq/fe-protocol3.c index d289f82285fea00d5de20542e43ea103493f9e58..c605bcd734c9fd76d15c893db6d66f727bb6d876 100644 *** a/src/interfaces/libpq/fe-protocol3.c --- b/src/interfaces/libpq/fe-protocol3.c *************** handleSyncLoss(PGconn *conn, char id, in *** 430,438 **** pqSaveErrorResult(conn); conn->asyncStatus = PGASYNC_READY; /* drop out of GetResult wait loop */ ! pqsecure_close(conn); ! closesocket(conn->sock); ! conn->sock = -1; conn->status = CONNECTION_BAD; /* No more connection to backend */ } --- 430,436 ---- pqSaveErrorResult(conn); conn->asyncStatus = PGASYNC_READY; /* drop out of GetResult wait loop */ ! pqDropConnection(conn); conn->status = CONNECTION_BAD; /* No more connection to backend */ } diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h index 2bac59c3d879ecabce42ceab3b5133df03a0886a..a9070ace73cead020b8b42f2720eebc771f39e6e 100644 *** a/src/interfaces/libpq/libpq-int.h --- b/src/interfaces/libpq/libpq-int.h *************** struct pg_conn *** 303,308 **** --- 303,310 ---- char *pgunixsocket; /* the Unix-domain socket that the server is * listening on; if NULL, uses a default * constructed from pgport */ + char *standalone_datadir; /* data directory for standalone backend */ + char *standalone_backend; /* executable for standalone backend */ char *pgtty; /* tty on which the backend messages is * displayed (OBSOLETE, NOT USED) */ char *connect_timeout; /* connection timeout (numeric string) */ *************** struct pg_conn *** 373,378 **** --- 375,383 ---- bool sigpipe_so; /* have we masked SIGPIPE via SO_NOSIGPIPE? */ bool sigpipe_flag; /* can we mask SIGPIPE via MSG_NOSIGNAL? */ + /* If we forked a child postgres process, its PID is kept here */ + pid_t postgres_pid; /* PID, or -1 if none */ + /* Transient state needed while establishing connection */ struct addrinfo *addrlist; /* list of possible backend addresses */ struct addrinfo *addr_cur; /* the one currently being tried */ *************** extern char *const pgresStatus[]; *** 488,493 **** --- 493,499 ---- /* === in fe-connect.c === */ + extern void pqDropConnection(PGconn *conn); extern int pqPacketSend(PGconn *conn, char pack_type, const void *buf, size_t buf_len); extern bool pqGetHomeDirectory(char *buf, int bufsize);
pgsql-hackers by date: