On Fri, May 15, 2015 at 3:53 PM, Robert Haas <robertmh...@gmail.com> wrote:
> On Thu, May 14, 2015 at 8:25 AM, Pavel Stehule <pavel.steh...@gmail.com> 
> wrote:
>> The documentation (or this feature) is broken still
>>
>> If dbname is NULL or dboid is InvalidOid, the session is not connected to
>> any particular database, but shared catalogs can be accessed. If username is
>> NULL or useroid is InvalidOid, the process will run as the superuser created
>> during initdb. A background worker can only call one of these two functions,
>> and only once. It is not possible to switch databases.
>>
>> But it fails with error:
>>
>> FATAL:  database 0 does not exist
>
> Ugh.  I think that's a bug.
>
> Patch attached.
>
> The test code I used to verify that this works is also attached.
>
> If there are no objections, I will commit and back-patch.

Oops.  Really attached this time.

-- 
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c
index debadf0..28a4966 100644
--- a/src/backend/utils/init/postinit.c
+++ b/src/backend/utils/init/postinit.c
@@ -827,7 +827,7 @@ InitPostgres(const char *in_dbname, Oid dboid, const char *username,
 		/* take database name from the caller, just for paranoia */
 		strlcpy(dbname, in_dbname, sizeof(dbname));
 	}
-	else
+	else if (OidIsValid(dboid))
 	{
 		/* caller specified database by OID */
 		HeapTuple	tuple;
@@ -847,6 +847,18 @@ InitPostgres(const char *in_dbname, Oid dboid, const char *username,
 		if (out_dbname)
 			strcpy(out_dbname, dbname);
 	}
+	else
+	{
+		/*
+		 * If this is a background worker not bound to any particular
+		 * database, we're done now.  Everything that follows only makes
+		 * sense if we are bound to a specific database.  We do need to
+		 * close the transaction we started before returning.
+		 */
+		if (!bootstrap)
+			CommitTransactionCommand();
+		return;
+	}
 
 	/* Now we can mark our PGPROC entry with the database ID */
 	/* (We assume this is an atomic store so no lock is needed) */
diff --git a/contrib/no_db_worker/Makefile b/contrib/no_db_worker/Makefile
new file mode 100644
index 0000000..2085c95
--- /dev/null
+++ b/contrib/no_db_worker/Makefile
@@ -0,0 +1,18 @@
+# contrib/no_db_worker
+
+MODULES = no_db_worker
+
+EXTENSION = no_db_worker
+DATA = no_db_worker--1.0.sql
+PGFILEDESC = "no_db_worker - background worker without database"
+
+ifdef USE_PGXS
+PG_CONFIG = pg_config
+PGXS := $(shell $(PG_CONFIG) --pgxs)
+include $(PGXS)
+else
+subdir = contrib/no_db_worker
+top_builddir = ../..
+include $(top_builddir)/src/Makefile.global
+include $(top_srcdir)/contrib/contrib-global.mk
+endif
diff --git a/contrib/no_db_worker/no_db_worker--1.0.sql b/contrib/no_db_worker/no_db_worker--1.0.sql
new file mode 100644
index 0000000..a38ec63
--- /dev/null
+++ b/contrib/no_db_worker/no_db_worker--1.0.sql
@@ -0,0 +1,7 @@
+/* contrib/no_db_worker/no_db_worker--1.0.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION no_db_worker" to load this file. \quit
+
+CREATE FUNCTION no_db_worker_launch() RETURNS pg_catalog.int4 STRICT
+AS 'MODULE_PATHNAME' LANGUAGE C;
diff --git a/contrib/no_db_worker/no_db_worker.c b/contrib/no_db_worker/no_db_worker.c
new file mode 100644
index 0000000..2a09bc4
--- /dev/null
+++ b/contrib/no_db_worker/no_db_worker.c
@@ -0,0 +1,103 @@
+/* -------------------------------------------------------------------------
+ *
+ * no_db_worker.c
+ *		A database worker that does not connect to any particular database.
+ *
+ * Copyright (C) 2015, PostgreSQL Global Development Group
+ *
+ * IDENTIFICATION
+ *		contrib/no_db_worker/no_db_worker.c
+ *
+ * -------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/relscan.h"
+#include "access/xact.h"
+#include "catalog/pg_database.h"
+#include "fmgr.h"
+#include "miscadmin.h"
+#include "postmaster/bgworker.h"
+#include "storage/ipc.h"
+#include "utils/rel.h"
+#include "utils/snapmgr.h"
+
+PG_MODULE_MAGIC;
+
+PG_FUNCTION_INFO_V1(no_db_worker_launch);
+
+extern void no_db_worker_main(Datum main_arg);
+
+void
+no_db_worker_main(Datum main_arg)
+{
+	Relation	rel;
+	HeapScanDesc scan;
+	HeapTuple	tup;
+
+	BackgroundWorkerInitializeConnection(NULL, NULL);
+
+	StartTransactionCommand();
+	(void) GetTransactionSnapshot();
+
+	rel = heap_open(DatabaseRelationId, AccessShareLock);
+	scan = heap_beginscan_catalog(rel, 0, NULL);
+
+	while (HeapTupleIsValid(tup = heap_getnext(scan, ForwardScanDirection)))
+	{
+		Form_pg_database pgdatabase = (Form_pg_database) GETSTRUCT(tup);
+
+		elog(LOG, "found database with OID %u and name \"%s\"",
+			HeapTupleGetOid(tup), NameStr(pgdatabase->datname));
+	}
+
+	elog(LOG, "done scanning pg_database");
+
+	heap_endscan(scan);
+	heap_close(rel, AccessShareLock);
+
+	proc_exit(1);
+}
+
+/*
+ * Dynamically launch an SPI worker.
+ */
+Datum
+no_db_worker_launch(PG_FUNCTION_ARGS)
+{
+	BackgroundWorker worker;
+	BackgroundWorkerHandle *handle;
+	BgwHandleStatus status;
+	pid_t		pid;
+
+	worker.bgw_flags = BGWORKER_SHMEM_ACCESS |
+		BGWORKER_BACKEND_DATABASE_CONNECTION;
+	worker.bgw_start_time = BgWorkerStart_RecoveryFinished;
+	worker.bgw_restart_time = BGW_NEVER_RESTART;
+	worker.bgw_main = NULL;		/* new worker might not have library loaded */
+	sprintf(worker.bgw_library_name, "no_db_worker");
+	sprintf(worker.bgw_function_name, "no_db_worker_main");
+	sprintf(worker.bgw_name, "no_db_worker");
+	worker.bgw_main_arg = 0;
+	/* set bgw_notify_pid so that we can use WaitForBackgroundWorkerStartup */
+	worker.bgw_notify_pid = MyProcPid;
+
+	if (!RegisterDynamicBackgroundWorker(&worker, &handle))
+		PG_RETURN_NULL();
+
+	status = WaitForBackgroundWorkerStartup(handle, &pid);
+
+	if (status == BGWH_STOPPED)
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_RESOURCES),
+				 errmsg("could not start background process"),
+			   errhint("More details may be available in the server log.")));
+	if (status == BGWH_POSTMASTER_DIED)
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_RESOURCES),
+			  errmsg("cannot start background processes without postmaster"),
+				 errhint("Kill all remaining database processes and restart the database.")));
+	Assert(status == BGWH_STARTED);
+
+	PG_RETURN_INT32(pid);
+}
diff --git a/contrib/no_db_worker/no_db_worker.control b/contrib/no_db_worker/no_db_worker.control
new file mode 100644
index 0000000..c9e0d0c
--- /dev/null
+++ b/contrib/no_db_worker/no_db_worker.control
@@ -0,0 +1,5 @@
+# no_db_worker extension
+comment = 'No-database background worker'
+default_version = '1.0'
+module_pathname = '$libdir/no_db_worker'
+relocatable = true
-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to