diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h index edced29..8e13cde 100644 --- a/src/include/miscadmin.h +++ b/src/include/miscadmin.h @@ -398,6 +398,9 @@ extern void pg_split_opts(char **argv, int *argcp, char *optstr); extern void InitializeMaxBackends(void); extern void InitPostgres(const char *in_dbname, Oid dboid, const char *username, char *out_dbname); +extern void InitPostgresPhase1(void); +extern void InitPostgresPhase2(const char *in_dbname, Oid dboid, + const char *username, char *out_dbname); extern void BaseInit(void); /* in utils/init/miscinit.c */ diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c index 2c7f0f1..92ff971 100644 --- a/src/backend/utils/init/postinit.c +++ b/src/backend/utils/init/postinit.c @@ -493,139 +493,166 @@ void InitPostgres(const char *in_dbname, Oid dboid, const char *username, char *out_dbname) { + /* Gain access to shared catalogs. */ + InitPostgresPhase1(); + + /* The autovacuum launcher is done here */ + if (IsAutoVacuumLauncherProcess()) + return; + + /* Perform client authentication, set MyDatabaseId, etc. */ + InitPostgresPhase2(in_dbname, dboid, username, out_dbname); +} + + +/* + * InitPostgresPhase1 + * + * Perform enough initialization to get us to a stage where access to the + * shared catalogs (at least, those that the relcache's formrdesc() helper + * knows how to bootstrap) can be done through the syscache. + */ +void InitPostgresPhase1() +{ bool bootstrap = IsBootstrapProcessingMode(); - bool am_superuser; - char *fullpath; - char dbname[NAMEDATALEN]; - elog(DEBUG3, "InitPostgres"); + elog(DEBUG3, "InitPostgresPhase1"); /* * Add my PGPROC struct to the ProcArray. * * Once I have done this, I am visible to other backends! */ InitProcessPhase2(); /* * Initialize my entry in the shared-invalidation manager's array of * per-backend data. * * Sets up MyBackendId, a unique backend identifier. */ MyBackendId = InvalidBackendId; SharedInvalBackendInit(false); if (MyBackendId > MaxBackends || MyBackendId <= 0) elog(FATAL, "bad backend ID: %d", MyBackendId); /* Now that we have a BackendId, we can participate in ProcSignal */ ProcSignalInit(MyBackendId); /* * Also set up timeout handlers needed for backend operation. We need * these in every case except bootstrap. */ if (!bootstrap) { RegisterTimeout(DEADLOCK_TIMEOUT, CheckDeadLock); RegisterTimeout(STATEMENT_TIMEOUT, StatementTimeoutHandler); RegisterTimeout(LOCK_TIMEOUT, LockTimeoutHandler); } /* * bufmgr needs another initialization call too */ InitBufferPoolBackend(); /* * Initialize local process's access to XLOG. */ if (IsUnderPostmaster) { /* * The postmaster already started the XLOG machinery, but we need to * call InitXLOGAccess(), if the system isn't in hot-standby mode. * This is handled by calling RecoveryInProgress and ignoring the * result. */ (void) RecoveryInProgress(); } else { /* * We are either a bootstrap process or a standalone backend. Either * way, start up the XLOG machinery, and register to have it closed * down at exit. */ StartupXLOG(); on_shmem_exit(ShutdownXLOG, 0); } /* * Initialize the relation cache and the system catalog caches. Note that * no catalog access happens here; we only set up the hashtable structure. * We must do this before starting a transaction because transaction abort * would try to touch these hashtables. */ RelationCacheInitialize(); InitCatalogCache(); InitPlanCache(); /* Initialize portal manager */ EnablePortalManager(); /* Initialize stats collection --- must happen before first xact */ if (!bootstrap) pgstat_initialize(); /* * Load relcache entries for the shared system catalogs. This must create * at least entries for pg_database and catalogs used for authentication. */ RelationCacheInitializePhase2(); /* * Set up process-exit callback to do pre-shutdown cleanup. This has to * be after we've initialized all the low-level modules like the buffer * manager, because during shutdown this has to run before the low-level * modules start to close down. On the other hand, we want it in place * before we begin our first transaction --- if we fail during the * initialization transaction, as is entirely possible, we need the * AbortTransaction call to clean up. */ on_shmem_exit(ShutdownPostgres, 0); +} - /* The autovacuum launcher is done here */ - if (IsAutoVacuumLauncherProcess()) - return; + +void +InitPostgresPhase2(const char *in_dbname, Oid dboid, const char *username, + char *out_dbname) +{ + bool bootstrap = IsBootstrapProcessingMode(); + bool am_superuser; + char *fullpath; + char dbname[NAMEDATALEN]; + + elog(DEBUG3, "InitPostgresPhase2"); /* * Start a new transaction here before first access to db, and get a * snapshot. We don't have a use for the snapshot itself, but we're * interested in the secondary effect that it sets RecentGlobalXmin. (This * is critical for anything that reads heap pages, because HOT may decide * to prune them even if the process doesn't attempt to modify any * tuples.) */ if (!bootstrap) { /* statement_timestamp must be set for timeouts to work correctly */ SetCurrentStatementStartTimestamp(); StartTransactionCommand(); /* * transaction_isolation will have been set to the default by the * above. If the default is "serializable", and we are in hot * standby, we will fail if we don't change it to something lower. * Fortunately, "read committed" is plenty good enough. */ XactIsoLevel = XACT_READ_COMMITTED; (void) GetTransactionSnapshot(); } /* * Perform client authentication if necessary, then figure out our * postgres user ID, and see if we are a superuser. diff --git a/src/include/postmaster/bgworker.h b/src/include/postmaster/bgworker.h index 0bb897b..802230b 100644 --- a/src/include/postmaster/bgworker.h +++ b/src/include/postmaster/bgworker.h @@ -92,15 +92,20 @@ extern bool RegisterDynamicBackgroundWorker(BackgroundWorker *worker); extern BackgroundWorker *MyBgworkerEntry; /* - * Connect to the specified database, as the specified user. Only a worker - * that passed BGWORKER_BACKEND_DATABASE_CONNECTION during registration may - * call this. - * - * If username is NULL, bootstrapping superuser is used. - * If dbname is NULL, connection is made to no specific database; - * only shared catalogs can be accessed. + * Initialize and open the specified database, as the specified user. Only a + * worker that passed BGWORKER_BACKEND_DATABASE_CONNECTION during registration + * may call these functions. + * + * Once InitializeConnection() has been called, shared catalogs may be + * accessed. [XXX: but only through the syscache interfaces?] + * + * After calling OpenDatabase(), the SPI functions may be used to access all + * relations. Once a database has been opened, it is not possible to + * disconnect and change to another database. If username is NULL, the + * bootstrapping superuser is used. */ -extern void BackgroundWorkerInitializeConnection(char *dbname, char *username); +extern void BackgroundWorkerInitializeConnection(); +extern void BackgroundWorkerOpenDatabase(char *dbname, char *username); /* Block/unblock signals in a background worker process */ extern void BackgroundWorkerBlockSignals(void); diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c index afdb53f..05b463f 100644 --- a/src/backend/postmaster/postmaster.c +++ b/src/backend/postmaster/postmaster.c @@ -5222,32 +5222,50 @@ int MaxLivePostmasterChildren(void) { return 2 * (MaxConnections + autovacuum_max_workers + 1 + max_worker_processes); } /* - * Connect background worker to a database. + * Connect background worker to the shared catalogs. */ void -BackgroundWorkerInitializeConnection(char *dbname, char *username) +BackgroundWorkerInitializeConnection() { BackgroundWorker *worker = MyBgworkerEntry; /* XXX is this the right errcode? */ if (!(worker->bgw_flags & BGWORKER_BACKEND_DATABASE_CONNECTION)) ereport(FATAL, (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), errmsg("database connection requirement not indicated during registration"))); - InitPostgres(dbname, InvalidOid, username, NULL); + InitPostgresPhase1(); +} + +/* + * Connect background worker to a database, and enter normal processing + * mode. + */ +void +BackgroundWorkerOpenDatabase(char *dbname, char *username) +{ + BackgroundWorker *worker = MyBgworkerEntry; + + /* XXX is this the right errcode? */ + if (!(worker->bgw_flags & BGWORKER_BACKEND_DATABASE_CONNECTION)) + ereport(FATAL, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("database connection requirement not indicated during registration"))); + + InitPostgresPhase2(dbname, InvalidOid, username, NULL); /* it had better not gotten out of "init" mode yet */ if (!IsInitProcessingMode()) ereport(ERROR, (errmsg("invalid processing mode in background worker"))); SetProcessingMode(NormalProcessing); } /* * Block/unblock signals in a background worker */