standalone_backend.2.patch

text/plain

Filename: standalone_backend.2.patch
Type: text/plain
Part: 0
Message: Re: Proof of concept: standalone backend with full FE/BE protocol

Patch

Same data as JSON: GET /api/v1/attachments/:id/patch the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes. API reference →
Format: context
File+
src/backend/main/main.c 2 0
src/backend/postmaster/postmaster.c 133 0
src/backend/tcop/postgres.c 8 0
src/include/postmaster/postmaster.h 3 0
src/interfaces/libpq/fe-connect.c 426 0
src/interfaces/libpq/libpq-int.h 9 0
*** a/src/backend/main/main.c
--- b/src/backend/main/main.c
***************
*** 191,196 **** main(int argc, char *argv[])
--- 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
*** a/src/backend/postmaster/postmaster.c
--- b/src/backend/postmaster/postmaster.c
***************
*** 463,468 **** typedef struct
--- 463,469 ----
  
  static void read_backend_variables(char *id, Port *port);
  static void restore_backend_variables(BackendParameters *param, Port *port);
+ static void read_standalone_child_variables(char *id, int *psock);
  
  #ifndef WIN32
  static bool save_backend_variables(BackendParameters *param, Port *port);
***************
*** 4268,4273 **** ExitPostmaster(int status)
--- 4269,4365 ----
  	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;
+ #ifdef WIN32
+ 	char        paramHandleStr[32];
+ #endif
+ 
+ 	/*
+ 	 * 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
+ 
+ #ifndef WIN32
+ 	/* 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])));
+ #else
+ /* The file descriptor of the client socket is the argument of --child */
+ 	if (sscanf(argv[1], "--child=%s", paramHandleStr) != 1)
+ 		ereport(FATAL,
+ 				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ 				 errmsg("invalid argument for --child: \"%s\"", argv[1])));
+ 
+ 	read_standalone_child_variables(paramHandleStr, &port->sock);
+ 
+ #endif
+ 
+ 	/* 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
   */
***************
*** 5073,5078 **** restore_backend_variables(BackendParameters *param, Port *port)
--- 5165,5211 ----
  }
  
  
+ #ifdef WIN32
+ static void
+ read_standalone_child_variables(char *id, int *psock)
+ {
+ 	HANDLE		paramHandle;
+ 	InheritableSocket param;
+ 	InheritableSocket *paramp;
+ 
+ /* Win32 version uses mapped file */
+ #ifdef _WIN64
+ 	paramHandle = (HANDLE) _atoi64(id);
+ #else
+ 	paramHandle = (HANDLE) atol(id);
+ #endif
+ 	paramp = MapViewOfFile(paramHandle, FILE_MAP_READ, 0, 0, 0);
+ 	if (!paramp)
+ 	{
+ 		write_stderr("could not map view of standalone child variables: error code %lu\n",
+ 					 GetLastError());
+ 		exit(1);
+ 	}
+ 
+ 	memcpy(&param, paramp, sizeof(InheritableSocket));
+ 
+ 	if (!UnmapViewOfFile(paramp))
+ 	{
+ 		write_stderr("could not unmap view of standalone child variables: error code %lu\n",
+ 					 GetLastError());
+ 		exit(1);
+ 	}
+ 
+ 	if (!CloseHandle(paramHandle))
+ 	{
+ 		write_stderr("could not close handle to standalone child parameter variables: error code %lu\n",
+ 					 GetLastError());
+ 		exit(1);
+ 	}
+ 
+ 	read_inheritable_socket(psock, &param);
+ }
+ #endif
  Size
  ShmemBackendArraySize(void)
  {
*** a/src/backend/tcop/postgres.c
--- b/src/backend/tcop/postgres.c
***************
*** 3257,3264 **** process_postgres_switches(int argc, char *argv[], GucContext ctx)
  	{
  		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--;
***************
*** 3522,3539 **** PostgresMain(int argc, char *argv[], const char *username)
  	 * 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);
*** a/src/include/postmaster/postmaster.h
--- b/src/include/postmaster/postmaster.h
***************
*** 47,52 **** extern int	postmaster_alive_fds[2];
--- 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);
*** 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>
***************
*** 259,264 **** static const PQconninfoOption PQconninfoOptions[] = {
--- 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}
***************
*** 360,365 **** pqDropConnection(PGconn *conn)
--- 367,393 ----
  	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)
+ 	{
+ #ifdef WIN32
+ 		while (WaitForSingleObject(conn->proc_handle, INFINITE) != WAIT_OBJECT_0)
+ 		{
+ 			_dosmaperr(GetLastError());
+ 			/* If interrupted by signal, keep waiting */
+ 			if (errno != EINTR)
+ 				break;
+ 		}
+ #else
+ 		while (waitpid(conn->postgres_pid, NULL, 0) < 0)
+ 		{
+             /* If interrupted by signal, keep waiting */
+ 			if (errno != EINTR)
+ 				break;
+ 		}
+ #endif
+ 	}
+ 	conn->postgres_pid = -1;
  	/* Discard any unread/unsent data */
  	conn->inStart = conn->inCursor = conn->inEnd = 0;
  	conn->outCount = 0;
***************
*** 643,648 **** fillPGconn(PGconn *conn, PQconninfoOption *connOptions)
--- 671,680 ----
  	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");
***************
*** 1311,1316 **** setKeepalivesWin32(PGconn *conn)
--- 1343,1682 ----
  #endif   /* SIO_KEEPALIVE_VALS */
  #endif   /* WIN32 */
  
+ #ifdef WIN32
+ int
+ pgsockpair(int handles[2], PGconn *conn)
+ {
+ 	SOCKET		s;
+ 	struct sockaddr_in serv_addr;
+ 	int			len = sizeof(serv_addr);
+ 
+ 	handles[0] = handles[1] = INVALID_SOCKET;
+ 
+ 	if ((s = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET)
+ 	{
+ 		char		sebuf[256];
+ 
+ 		appendPQExpBuffer(&conn->errorMessage,
+ 				   libpq_gettext("pgsockpair could not create socket: %s\n"),
+ 						  SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
+ 
+ 		return -1;
+ 	}
+ 
+ 	memset((void *) &serv_addr, 0, sizeof(serv_addr));
+ 	serv_addr.sin_family = AF_INET;
+ 	serv_addr.sin_port = htons(0);
+ 	serv_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ 	if (bind(s, (SOCKADDR *) & serv_addr, len) == SOCKET_ERROR)
+ 	{
+ 		char		sebuf[256];
+ 
+ 		appendPQExpBuffer(&conn->errorMessage,
+ 						  libpq_gettext("pgsockpair could not bind: %s\n"),
+ 						  SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
+ 
+ 		closesocket(s);
+ 		return -1;
+ 	}
+ 	if (listen(s, 1) == SOCKET_ERROR)
+ 	{
+ 		char		sebuf[256];
+ 
+ 		appendPQExpBuffer(&conn->errorMessage,
+ 						  libpq_gettext("pgsockpair could not listen: %s\n"),
+ 						  SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
+ 		closesocket(s);
+ 		return -1;
+ 	}
+ 	if (getsockname(s, (SOCKADDR *) & serv_addr, &len) == SOCKET_ERROR)
+ 	{
+ 		char		sebuf[256];
+ 
+ 		appendPQExpBuffer(&conn->errorMessage,
+ 					 libpq_gettext("pgsockpair could not getsockname: %s\n"),
+ 						  SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
+ 		closesocket(s);
+ 		return -1;
+ 	}
+ 	if ((handles[1] = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET)
+ 	{
+ 		char		sebuf[256];
+ 
+ 		appendPQExpBuffer(&conn->errorMessage,
+ 				 libpq_gettext("pgsockpair could not create socket 2: %s\n"),
+ 						  SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
+ 
+ 		closesocket(s);
+ 		return -1;
+ 	}
+ 
+ 	if (connect(handles[1], (SOCKADDR *) & serv_addr, len) == SOCKET_ERROR)
+ 	{
+ 		char		sebuf[256];
+ 
+ 		appendPQExpBuffer(&conn->errorMessage,
+ 				  libpq_gettext("pgsockpair could not connect socket: %s\n"),
+ 						  SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
+ 		closesocket(s);
+ 		return -1;
+ 	}
+ 	if ((handles[0] = accept(s, (SOCKADDR *) & serv_addr, &len)) == INVALID_SOCKET)
+ 	{
+ 		char		sebuf[256];
+ 
+ 		appendPQExpBuffer(&conn->errorMessage,
+ 				   libpq_gettext("pgsockpair could not accept socket: %s\n"),
+ 						  SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
+ 		closesocket(handles[1]);
+ 		handles[1] = INVALID_SOCKET;
+ 		closesocket(s);
+ 		return -1;
+ 	}
+ 	closesocket(s);
+ 	return 0;
+ }
+ 
+ /*
+  * 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, PGconn *conn)
+ {
+ 	int			socks[2];
+ 	char		newfdarg[32];
+ 	STARTUPINFO si;
+ 	PROCESS_INFORMATION pi;
+ 	int			i;
+ 	int			j;
+ 	char		cmdLine[MAXPGPATH * 2];
+ 	typedef struct
+ 	{
+ 		SOCKET		origsocket; /* Original socket value, or PGINVALID_SOCKET
+ 								 * if not a socket */
+ 		WSAPROTOCOL_INFO wsainfo;
+ 	} InheritableSocket;
+ 	InheritableSocket *param;
+ 	HANDLE		paramHandle;
+ 	SECURITY_ATTRIBUTES sa;
+ 	char		paramHandleStr[32];
+ 
+ 
+ 	if (pgsockpair(socks, conn) < 0)
+ 		return -1;
+ 
+ 	/* Set up shared memory for parameter passing */
+ 	ZeroMemory(&sa, sizeof(sa));
+ 	sa.nLength = sizeof(sa);
+ 	sa.bInheritHandle = TRUE;
+ 	paramHandle = CreateFileMapping(INVALID_HANDLE_VALUE,
+ 									&sa,
+ 									PAGE_READWRITE,
+ 									0,
+ 									sizeof(InheritableSocket),
+ 									NULL);
+ 	if (paramHandle == INVALID_HANDLE_VALUE)
+ 	{
+ 		char		sebuf[256];
+ 
+ 		appendPQExpBuffer(&conn->errorMessage,
+ 						  libpq_gettext("could not create standalone backend parameter file mapping: %s\n"),
+ 						  SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
+ 		return -1;
+ 	}
+ 
+ 	param = MapViewOfFile(paramHandle, FILE_MAP_WRITE, 0, 0, sizeof(InheritableSocket));
+ 	if (!param)
+ 	{
+ 		char		sebuf[256];
+ 
+ 		appendPQExpBuffer(&conn->errorMessage,
+ 			   libpq_gettext("could not map backend parameter memory: %s\n"),
+ 						  SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
+ 
+ 		CloseHandle(paramHandle);
+ 		return -1;
+ 	}
+ 
+ 	/* Insert temp file name instead of sockid incase of windows */
+ #ifdef _WIN64
+ 	sprintf(paramHandleStr, "%llu", (LONG_PTR) paramHandle);
+ #else
+ 	sprintf(paramHandleStr, "%lu", (DWORD) paramHandle);
+ #endif
+ 
+ 	snprintf(newfdarg, sizeof(newfdarg), "%s%s", argv[fdarg], paramHandleStr);
+ 	argv[fdarg] = newfdarg;
+ 
+ 	/* Format the cmd line */
+ 	cmdLine[sizeof(cmdLine) - 1] = '\0';
+ 	cmdLine[sizeof(cmdLine) - 2] = '\0';
+ 	snprintf(cmdLine, sizeof(cmdLine) - 1, "\"%s\"", argv[0]);
+ 
+ 	i = 0;
+ 	while (argv[++i] != NULL)
+ 	{
+ 		j = strlen(cmdLine);
+ 		snprintf(cmdLine + j, sizeof(cmdLine) - 1 - j, " \"%s\"", argv[i]);
+ 	}
+ 	if (cmdLine[sizeof(cmdLine) - 2] != '\0')
+ 	{
+ 		appendPQExpBuffer(&conn->errorMessage,
+ 					libpq_gettext("subprocess command line too long: %s\n"));
+ 
+ 		return -1;
+ 	}
+ 
+ 	memset(&pi, 0, sizeof(pi));
+ 	memset(&si, 0, sizeof(si));
+ 	si.cb = sizeof(si);
+ 
+ 	if (!CreateProcess(NULL, cmdLine, NULL, NULL, TRUE, CREATE_SUSPENDED,
+ 					   NULL, NULL, &si, &pi))
+ 	{
+ 		char		sebuf[256];
+ 
+ 		appendPQExpBuffer(&conn->errorMessage,
+ 						  libpq_gettext("CreateProcess call failed: %s\n"),
+ 						  SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
+ 
+ 		return -1;
+ 	}
+ 
+ 
+ 	param->origsocket = socks[0];
+ 	if (socks[0] != 0 && socks[0] != PGINVALID_SOCKET)
+ 	{
+ 		/* Actual socket */
+ 		if (WSADuplicateSocket(socks[0], pi.dwProcessId, &param->wsainfo) != 0)
+ 		{
+ 			char		sebuf[256];
+ 
+ 			if (!TerminateProcess(pi.hProcess, 255))
+ 			{
+ 				appendPQExpBuffer(&conn->errorMessage,
+ 				libpq_gettext("could not terminate unstarted process: %s\n"),
+ 							SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
+ 
+ 				CloseHandle(pi.hProcess);
+ 				CloseHandle(pi.hThread);
+ 				return -1;
+ 			}
+ 
+ 			appendPQExpBuffer(&conn->errorMessage,
+ 					   libpq_gettext("could not duplicate the socket: %s\n"),
+ 							SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
+ 
+ 			CloseHandle(pi.hProcess);
+ 			CloseHandle(pi.hThread);
+ 			return -1;
+ 		}
+ 	}
+ 
+ 	/*
+ 	 * Drop the parameter shared memory that is now inherited to the
+ 	 * standalone chile
+ 	 */
+ 	if (!UnmapViewOfFile(param))
+ 	{
+ 		char		sebuf[256];
+ 
+ 		fprintf(stderr,
+ 				libpq_gettext("WARNING: could not unmap view of standalone child variables: %s\n"),
+ 				SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
+ 	}
+ 
+ 	if (!CloseHandle(paramHandle))
+ 	{
+ 		char		sebuf[256];
+ 
+ 		fprintf(stderr,
+ 				libpq_gettext("WARNING: could not close handle of standalone child variables: %s\n"),
+ 				SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
+ 	}
+ 
+ 	/*
+ 	 * Now that the socket information is shared, we start the child thread
+ 	 */
+ 	if (ResumeThread(pi.hThread) == -1)
+ 	{
+ 		char		sebuf[256];
+ 
+ 		if (!TerminateProcess(pi.hProcess, 255))
+ 		{
+ 			appendPQExpBuffer(&conn->errorMessage,
+ 				libpq_gettext("could not terminate unstarted process: %s\n"),
+ 							SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
+ 
+ 			CloseHandle(pi.hProcess);
+ 			CloseHandle(pi.hThread);
+ 			return -1;
+ 		}
+ 		appendPQExpBuffer(&conn->errorMessage,
+ 		 libpq_gettext("could not resume thread of unstarted process: %s\n"),
+ 						  SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
+ 		CloseHandle(pi.hProcess);
+ 		CloseHandle(pi.hThread);
+ 		return -1;
+ 	}
+ 	/* Don't close pi.hProcess here - the wait thread needs access to it */
+ 	CloseHandle(pi.hThread);
+ 
+ 	conn->sock = socks[1];
+ 	conn->proc_handle = pi.hProcess;
+ 	return pi.dwProcessId;
+ }
+ #else
+ 
+ /*
+  * 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;
+ }
+ #endif
+ 
  /* ----------
   * connectDBStart -
   *		Begin the process of making a connection to the backend.
***************
*** 1339,1344 **** connectDBStart(PGconn *conn)
--- 1705,1765 ----
  	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;
+ 
+ #ifdef WIN32
+ 		conn->postgres_pid = fork_backend_child(be_argv, 1, conn);
+ 		if (conn->postgres_pid < 0)
+ 			goto connect_errReturn;
+ #else
+ 		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;
+ 		}
+ #endif
+ 
+ 		/*
+ 		 * 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.
  	 */
  
***************
*** 2708,2713 **** makeEmptyPGconn(void)
--- 3129,3135 ----
  	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;
***************
*** 2784,2789 **** freePGconn(PGconn *conn)
--- 3206,3215 ----
  		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)
*** a/src/interfaces/libpq/libpq-int.h
--- b/src/interfaces/libpq/libpq-int.h
***************
*** 303,308 **** struct pg_conn
--- 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) */
***************
*** 373,378 **** struct pg_conn
--- 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 */
***************
*** 415,420 **** struct pg_conn
--- 420,429 ----
  	/* Status for asynchronous result construction */
  	PGresult   *result;			/* result being constructed */
  	PGresult   *next_result;	/* next result (used in single-row mode) */
+ #ifdef WIN32
+ 	/* If we forked a child postgres process, process handle */
+     HANDLE      proc_handle;
+ #endif
  
  	/* Assorted state for SSL, GSS, etc */