forked from huawei/openGauss-server
2030 lines
62 KiB
C++
2030 lines
62 KiB
C++
/* -------------------------------------------------------------------------
|
|
*
|
|
* postinit.c
|
|
* postgres initialization utilities
|
|
*
|
|
* Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
|
*
|
|
*
|
|
* IDENTIFICATION
|
|
* src/backend/utils/init/postinit.c
|
|
*
|
|
*
|
|
* -------------------------------------------------------------------------
|
|
*/
|
|
#include "postgres.h"
|
|
#include "knl/knl_variable.h"
|
|
|
|
#include <ctype.h>
|
|
#include <fcntl.h>
|
|
#include <unistd.h>
|
|
|
|
#include "access/heapam.h"
|
|
#include "access/sysattr.h"
|
|
#include "access/xact.h"
|
|
#include "access/xlog.h"
|
|
#include "catalog/catalog.h"
|
|
#include "catalog/indexing.h"
|
|
#include "catalog/namespace.h"
|
|
#include "catalog/pg_authid.h"
|
|
#include "catalog/pg_database.h"
|
|
#include "catalog/pg_db_role_setting.h"
|
|
#include "catalog/pg_tablespace.h"
|
|
#include "catalog/pg_hashbucket_fn.h"
|
|
#include "executor/executor.h"
|
|
#include "executor/execStream.h"
|
|
#include "executor/nodeModifyTable.h"
|
|
#include "job/job_scheduler.h"
|
|
#include "job/job_worker.h"
|
|
#include "libpq/auth.h"
|
|
#include "libpq/ip.h"
|
|
#include "libpq/libpq-be.h"
|
|
#include "mb/pg_wchar.h"
|
|
#include "miscadmin.h"
|
|
#include "pgstat.h"
|
|
#include "pgxc/execRemote.h"
|
|
#include "pgxc/poolmgr.h"
|
|
#include "pgxc/groupmgr.h"
|
|
#include "pgxc/pgxc.h"
|
|
#include "pgxc/pgxcnode.h"
|
|
#include "postmaster/autovacuum.h"
|
|
#include "postmaster/postmaster.h"
|
|
#include "replication/catchup.h"
|
|
#include "replication/walsender.h"
|
|
#include "storage/bufmgr.h"
|
|
#include "storage/fd.h"
|
|
#include "storage/ipc.h"
|
|
#include "storage/lmgr.h"
|
|
#include "storage/proc.h"
|
|
#include "storage/procarray.h"
|
|
#include "storage/procsignal.h"
|
|
#include "storage/sinvaladt.h"
|
|
#include "storage/smgr.h"
|
|
#include "tcop/tcopprot.h"
|
|
#include "threadpool/threadpool.h"
|
|
#include "utils/acl.h"
|
|
#include "utils/fmgroids.h"
|
|
#include "utils/guc.h"
|
|
#include "utils/pg_locale.h"
|
|
#include "utils/portal.h"
|
|
#include "utils/postinit.h"
|
|
#include "utils/ps_status.h"
|
|
#include "utils/snapmgr.h"
|
|
#include "utils/syscache.h"
|
|
#include "utils/tqual.h"
|
|
#include "auditfuncs.h"
|
|
#include "gssignal/gs_signal.h"
|
|
#include "storage/cucache_mgr.h"
|
|
#include "alarm/alarm.h"
|
|
#include "commands/user.h"
|
|
#include "instruments/snapshot.h"
|
|
#include "instruments/instr_user.h"
|
|
#include "instruments/percentile.h"
|
|
#include "instruments/instr_workload.h"
|
|
|
|
#ifdef PGXC
|
|
#include "catalog/pgxc_node.h"
|
|
#include "utils/rel.h"
|
|
#include "utils/rel_gs.h"
|
|
#include "utils/lsyscache.h"
|
|
static void AlterPgxcNodePort(void);
|
|
#endif
|
|
|
|
bool ConnAuthMethodCorrect = true;
|
|
Alarm alarmItemTooManyDatabaseConn[1] = {ALM_AI_Unknown, ALM_AS_Normal, 0, 0, 0, 0, {0}, {0}, NULL};
|
|
|
|
static HeapTuple GetDatabaseTuple(const char* dbname);
|
|
static HeapTuple GetDatabaseTupleByOid(Oid dboid);
|
|
static void PerformAuthentication(Port* port);
|
|
static void CheckMyDatabase(const char* name, bool am_superuser);
|
|
static void InitCommunication(void);
|
|
static bool ThereIsAtLeastOneRole(void);
|
|
static void process_startup_options(Port* port, bool am_superuser);
|
|
static void process_pgoptions(Port* port, bool am_superuser);
|
|
static void process_settings(Oid databaseid, Oid roleid);
|
|
extern bool StreamThreadAmI();
|
|
extern bool StreamTopConsumerAmI();
|
|
void ShutdownPostgres(int code, Datum arg);
|
|
|
|
#ifdef ENABLE_MULTIPLE_NODES
|
|
/*
|
|
* init cache api for pgxc nodes
|
|
*/
|
|
extern void PGXC_Cache_Init();
|
|
#endif
|
|
|
|
AlarmCheckResult ConnAuthMethodChecker(Alarm* alarm, AlarmAdditionalParam* additionalParam)
|
|
{
|
|
if (true == ConnAuthMethodCorrect) {
|
|
// fill the resume message
|
|
WriteAlarmAdditionalInfo(
|
|
additionalParam, g_instance.attr.attr_common.PGXCNodeName, "", "", alarm, ALM_AT_Resume);
|
|
return ALM_ACR_Normal;
|
|
} else {
|
|
// fill the alarm message
|
|
WriteAlarmAdditionalInfo(additionalParam,
|
|
g_instance.attr.attr_common.PGXCNodeName,
|
|
"",
|
|
"",
|
|
alarm,
|
|
ALM_AT_Fault,
|
|
g_instance.attr.attr_common.PGXCNodeName);
|
|
return ALM_ACR_Abnormal;
|
|
}
|
|
}
|
|
|
|
void ReportAlarmTooManyDatabaseConn(const char* dbName)
|
|
{
|
|
AlarmAdditionalParam temp_additional_param;
|
|
|
|
// Initialize the alarm item
|
|
AlarmItemInitialize(alarmItemTooManyDatabaseConn,
|
|
ALM_AI_TooManyDatabaseConn,
|
|
alarmItemTooManyDatabaseConn->stat,
|
|
NULL,
|
|
alarmItemTooManyDatabaseConn->lastReportTime,
|
|
alarmItemTooManyDatabaseConn->reportCount);
|
|
// fill the alarm message
|
|
WriteAlarmAdditionalInfo(&temp_additional_param,
|
|
g_instance.attr.attr_common.PGXCNodeName,
|
|
const_cast<char *>(dbName),
|
|
"",
|
|
alarmItemTooManyDatabaseConn,
|
|
ALM_AT_Fault,
|
|
const_cast<char *>(dbName));
|
|
// report the alarm
|
|
AlarmReporter(alarmItemTooManyDatabaseConn, ALM_AT_Fault, &temp_additional_param);
|
|
}
|
|
|
|
void ReportResumeTooManyDatabaseConn(const char* dbName)
|
|
{
|
|
AlarmAdditionalParam temp_additional_param;
|
|
|
|
// Initialize the alarm item
|
|
AlarmItemInitialize(alarmItemTooManyDatabaseConn,
|
|
ALM_AI_TooManyDatabaseConn,
|
|
alarmItemTooManyDatabaseConn->stat,
|
|
NULL,
|
|
alarmItemTooManyDatabaseConn->lastReportTime,
|
|
alarmItemTooManyDatabaseConn->reportCount);
|
|
// fill the alarm message
|
|
WriteAlarmAdditionalInfo(&temp_additional_param,
|
|
g_instance.attr.attr_common.PGXCNodeName,
|
|
const_cast<char *>(dbName),
|
|
"",
|
|
alarmItemTooManyDatabaseConn,
|
|
ALM_AT_Resume);
|
|
// report the alarm
|
|
AlarmReporter(alarmItemTooManyDatabaseConn, ALM_AT_Resume, &temp_additional_param);
|
|
}
|
|
|
|
/*
|
|
* GetDatabaseTuple -- fetch the pg_database row for a database
|
|
*
|
|
* This is used during backend startup when we don't yet have any access to
|
|
* system catalogs in general. In the worst case, we can seqscan pg_database
|
|
* using nothing but the hard-wired descriptor that relcache.c creates for
|
|
* pg_database. In more typical cases, relcache.c was able to load
|
|
* descriptors for both pg_database and its indexes from the shared relcache
|
|
* cache file, and so we can do an indexscan. u_sess->relcache_cxt.criticalSharedRelcachesBuilt
|
|
* tells whether we got the cached descriptors.
|
|
*/
|
|
static HeapTuple GetDatabaseTuple(const char* dbname)
|
|
{
|
|
HeapTuple tuple;
|
|
Relation relation;
|
|
SysScanDesc scan;
|
|
ScanKeyData key[1];
|
|
|
|
/*
|
|
* form a scan key
|
|
*/
|
|
ScanKeyInit(&key[0], Anum_pg_database_datname, BTEqualStrategyNumber, F_NAMEEQ, CStringGetDatum(dbname));
|
|
|
|
/*
|
|
* Open pg_database and fetch a tuple. Force heap scan if we haven't yet
|
|
* built the critical shared relcache entries (i.e., we're starting up
|
|
* without a shared relcache cache file).
|
|
*/
|
|
relation = heap_open(DatabaseRelationId, AccessShareLock);
|
|
scan = systable_beginscan(
|
|
relation, DatabaseNameIndexId, u_sess->relcache_cxt.criticalSharedRelcachesBuilt, SnapshotNow, 1, key);
|
|
|
|
tuple = systable_getnext(scan);
|
|
|
|
/* Must copy tuple before releasing buffer */
|
|
if (HeapTupleIsValid(tuple)) {
|
|
tuple = heap_copytuple(tuple);
|
|
}
|
|
/* all done */
|
|
systable_endscan(scan);
|
|
heap_close(relation, AccessShareLock);
|
|
|
|
return tuple;
|
|
}
|
|
|
|
/*
|
|
* GetDatabaseTupleByOid -- as above, but search by database OID
|
|
*/
|
|
static HeapTuple GetDatabaseTupleByOid(Oid dboid)
|
|
{
|
|
HeapTuple tuple;
|
|
Relation relation;
|
|
SysScanDesc scan;
|
|
ScanKeyData key[1];
|
|
|
|
/*
|
|
* form a scan key
|
|
*/
|
|
ScanKeyInit(&key[0], ObjectIdAttributeNumber, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(dboid));
|
|
|
|
/*
|
|
* Open pg_database and fetch a tuple. Force heap scan if we haven't yet
|
|
* built the critical shared relcache entries (i.e., we're starting up
|
|
* without a shared relcache cache file).
|
|
*/
|
|
relation = heap_open(DatabaseRelationId, AccessShareLock);
|
|
scan = systable_beginscan(
|
|
relation, DatabaseOidIndexId, u_sess->relcache_cxt.criticalSharedRelcachesBuilt, SnapshotNow, 1, key);
|
|
|
|
tuple = systable_getnext(scan);
|
|
|
|
/* Must copy tuple before releasing buffer */
|
|
if (HeapTupleIsValid(tuple)) {
|
|
tuple = heap_copytuple(tuple);
|
|
}
|
|
/* all done */
|
|
systable_endscan(scan);
|
|
heap_close(relation, AccessShareLock);
|
|
|
|
return tuple;
|
|
}
|
|
|
|
/*
|
|
* PerformAuthentication -- authenticate a remote client
|
|
*
|
|
* returns: nothing. Will not return at all if there's any failure.
|
|
*/
|
|
static void PerformAuthentication(Port* port)
|
|
{
|
|
sigset_t old_sigset;
|
|
|
|
/* This should be set already, but let's make sure */
|
|
u_sess->ClientAuthInProgress = true; /* limit visibility of log messages */
|
|
|
|
/*
|
|
* In EXEC_BACKEND case, we didn't inherit the contents of pg_hba.conf
|
|
* etcetera from the postmaster, and have to load them ourselves. Note we
|
|
* are loading them into the startup transaction's memory context, not
|
|
* t_thrd.mem_cxt.postmaster_mem_cxt, but that shouldn't matter.
|
|
*/
|
|
#ifdef EXEC_BACKEND
|
|
|
|
int loadhbaCount = 0;
|
|
while (!load_hba()) {
|
|
loadhbaCount++;
|
|
pg_usleep(200000L); // slepp 200ms for reload
|
|
if (loadhbaCount >= 3) {
|
|
/*
|
|
* It makes no sense to continue if we fail to load the HBA file,
|
|
* since there is no way to connect to the database in this case.
|
|
*/
|
|
ereport(FATAL, (errmsg("could not load pg_hba.conf")));
|
|
}
|
|
}
|
|
|
|
/*
|
|
* It is ok to continue if we fail to load the IDENT file, although it
|
|
* means that we do not exist any authentication mapping between sys_user
|
|
* and database user. load_ident() already logged the details of error
|
|
* to the log.
|
|
*/
|
|
(void)load_ident();
|
|
|
|
#endif
|
|
|
|
/*
|
|
* Set up a timeout in case a buggy or malicious client fails to respond
|
|
* during authentication. Since we're inside a transaction and might do
|
|
* database access, we have to use the statement_timeout infrastructure.
|
|
*/
|
|
if (!enable_sig_alarm(u_sess->attr.attr_security.AuthenticationTimeout * 1000, true)) {
|
|
ereport(FATAL, (errmsg("could not set timer for authorization timeout")));
|
|
}
|
|
/*
|
|
* Unblock SIGUSR2 so that SIGALRM can be triggered when perform authentication timeout.
|
|
*/
|
|
old_sigset = gs_signal_unblock_sigusr2();
|
|
|
|
/*
|
|
* Now perform authentication exchange.
|
|
*/
|
|
ClientAuthentication(port); /* might not return, if failure */
|
|
|
|
/*
|
|
* recover the signal mask before call ClientAuthentication.
|
|
*/
|
|
gs_signal_recover_mask(old_sigset);
|
|
|
|
/*
|
|
* Done with authentication. Disable the timeout, and log if needed.
|
|
*/
|
|
if (!disable_sig_alarm(true)) {
|
|
ereport(FATAL, (errmsg("could not disable timer for authorization timeout")));
|
|
}
|
|
|
|
if (u_sess->attr.attr_storage.Log_connections) {
|
|
if (AM_WAL_SENDER) {
|
|
ereport(LOG, (errmsg("replication connection authorized: user=%s", port->user_name)));
|
|
} else {
|
|
ereport(LOG, (errmsg("connection authorized: user=%s database=%s", port->user_name, port->database_name)));
|
|
}
|
|
}
|
|
|
|
/* INSTR: update user login counter */
|
|
if (IsUnderPostmaster && !IsBootstrapProcessingMode() && !dummyStandbyMode) {
|
|
InstrUpdateUserLogCounter(true);
|
|
}
|
|
|
|
set_ps_display("startup", false);
|
|
|
|
u_sess->ClientAuthInProgress = false; /* client_min_messages is active now */
|
|
u_sess->misc_cxt.authentication_finished = true;
|
|
}
|
|
|
|
// Check if the connection is local
|
|
//
|
|
static bool CheckLocalConnection()
|
|
{
|
|
Assert(u_sess->proc_cxt.MyProcPort != NULL);
|
|
if (IS_AF_UNIX(u_sess->proc_cxt.MyProcPort->raddr.addr.ss_family) ||
|
|
strcmp(u_sess->proc_cxt.MyProcPort->remote_host, "127.0.0.1") == 0 ||
|
|
strcmp(u_sess->proc_cxt.MyProcPort->remote_host, "::1") == 0) {
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static void SaveSessionEncodingInfo(Form_pg_database dbform)
|
|
{
|
|
errno_t rc;
|
|
rc = strncpy_s(NameStr(u_sess->mb_cxt.datctype), NAMEDATALEN, NameStr(dbform->datctype), NAMEDATALEN - 1);
|
|
securec_check(rc, "\0", "\0");
|
|
rc = strncpy_s(NameStr(u_sess->mb_cxt.datcollate), NAMEDATALEN, NameStr(dbform->datcollate), NAMEDATALEN - 1);
|
|
securec_check(rc, "\0", "\0");
|
|
rc = strncpy_s(NameStr(t_thrd.port_cxt.cur_datctype), NAMEDATALEN, NameStr(dbform->datctype), NAMEDATALEN - 1);
|
|
securec_check(rc, "\0", "\0");
|
|
rc = strncpy_s(NameStr(t_thrd.port_cxt.cur_datcollate), NAMEDATALEN, NameStr(dbform->datcollate), NAMEDATALEN - 1);
|
|
securec_check(rc, "\0", "\0");
|
|
}
|
|
|
|
/*
|
|
* CheckMyDatabase -- fetch information from the pg_database entry for our DB
|
|
*/
|
|
static void CheckMyDatabase(const char* name, bool am_superuser)
|
|
{
|
|
HeapTuple tup;
|
|
Form_pg_database db_form;
|
|
char* collate = NULL;
|
|
char* ctype = NULL;
|
|
|
|
/* Fetch our pg_database row normally, via syscache */
|
|
tup = SearchSysCache1(DATABASEOID, ObjectIdGetDatum(u_sess->proc_cxt.MyDatabaseId));
|
|
|
|
if (!HeapTupleIsValid(tup)) {
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
|
errmsg("cache lookup failed for database %u", u_sess->proc_cxt.MyDatabaseId)));
|
|
}
|
|
|
|
db_form = (Form_pg_database)GETSTRUCT(tup);
|
|
|
|
/* This recheck is strictly paranoia */
|
|
if (strcmp(name, NameStr(db_form->datname)) != 0) {
|
|
ereport(FATAL,
|
|
(errcode(ERRCODE_UNDEFINED_DATABASE),
|
|
errmsg("database \"%s\" has disappeared from pg_database", name),
|
|
errdetail("Database OID %u now seems to belong to \"%s\".",
|
|
u_sess->proc_cxt.MyDatabaseId,
|
|
NameStr(db_form->datname))));
|
|
}
|
|
/*
|
|
* Check permissions to connect to the database.
|
|
*
|
|
* These checks are not enforced when in standalone mode, so that there is
|
|
* a way to recover from disabling all access to all databases, for
|
|
* example "UPDATE pg_database SET datallowconn = false;".
|
|
*
|
|
* We do not enforce them for autovacuum worker processes either.
|
|
*/
|
|
if (IsUnderPostmaster && !IsAutoVacuumWorkerProcess()) {
|
|
/*
|
|
* Check that the database is currently allowing connections.
|
|
*/
|
|
if (!db_form->datallowconn && (u_sess->attr.attr_common.upgrade_mode == 0 || !am_superuser)) {
|
|
ereport(FATAL,
|
|
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
|
|
errmsg("database \"%s\" is not currently accepting connections", name)));
|
|
}
|
|
/*
|
|
* Check privilege to connect to the database. (The am_superuser test
|
|
* is redundant, but since we have the flag, might as well check it
|
|
* and save a few cycles.)
|
|
*/
|
|
if (!am_superuser &&
|
|
pg_database_aclcheck(u_sess->proc_cxt.MyDatabaseId, GetUserId(), ACL_CONNECT) != ACLCHECK_OK) {
|
|
ereport(FATAL,
|
|
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
|
|
errmsg("permission denied for database \"%s\"", name),
|
|
errdetail("User does not have CONNECT privilege.")));
|
|
}
|
|
/*
|
|
* Check connection limit for this database.
|
|
*
|
|
* There is a race condition here --- we create our PGPROC before
|
|
* checking for other PGPROCs. If two backends did this at about the
|
|
* same time, they might both think they were over the limit, while
|
|
* ideally one should succeed and one fail. Getting that to work
|
|
* exactly seems more trouble than it is worth, however; instead we
|
|
* just document that the connection limit is approximate.
|
|
*/
|
|
if (db_form->datconnlimit >= 0 && !am_superuser &&
|
|
CountDBBackends(u_sess->proc_cxt.MyDatabaseId) > db_form->datconnlimit) {
|
|
ReportAlarmTooManyDatabaseConn(name);
|
|
|
|
ereport(FATAL,
|
|
(errcode(ERRCODE_TOO_MANY_CONNECTIONS), errmsg("too many connections for database \"%s\"", name)));
|
|
} else if (!am_superuser) {
|
|
ReportResumeTooManyDatabaseConn(name);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* OK, we're golden. Next to-do item is to save the encoding info out of
|
|
* the pg_database tuple.
|
|
*/
|
|
SetDatabaseEncoding(db_form->encoding);
|
|
/* Record it as a GUC internal option, too */
|
|
SetConfigOption("server_encoding", GetDatabaseEncodingName(), PGC_INTERNAL, PGC_S_OVERRIDE);
|
|
/* If we have no other source of client_encoding, use server encoding */
|
|
SetConfigOption("client_encoding", GetDatabaseEncodingName(), PGC_BACKEND, PGC_S_DYNAMIC_DEFAULT);
|
|
|
|
// if we are identical no bother to set that in thread pool settings.
|
|
if (!IS_THREAD_POOL_WORKER || strcmp(NameStr(db_form->datcollate), NameStr(t_thrd.port_cxt.cur_datcollate)) != 0 ||
|
|
strcmp(NameStr(db_form->datctype), NameStr(t_thrd.port_cxt.cur_datctype)) != 0) {
|
|
/* assign locale variables */
|
|
collate = NameStr(db_form->datcollate);
|
|
ctype = NameStr(db_form->datctype);
|
|
|
|
if (pg_perm_setlocale(LC_COLLATE, collate) == NULL) {
|
|
ereport(FATAL,
|
|
(errmsg("database locale is incompatible with operating system"),
|
|
errdetail("The database was initialized with LC_COLLATE \"%s\", "
|
|
" which is not recognized by setlocale().",
|
|
collate),
|
|
errhint("Recreate the database with another locale or install the missing locale.")));
|
|
}
|
|
|
|
if (pg_perm_setlocale(LC_CTYPE, ctype) == NULL) {
|
|
ereport(FATAL,
|
|
(errmsg("database locale is incompatible with operating system"),
|
|
errdetail("The database was initialized with LC_CTYPE \"%s\", "
|
|
" which is not recognized by setlocale().",
|
|
ctype),
|
|
errhint("Recreate the database with another locale or install the missing locale.")));
|
|
}
|
|
|
|
/* Make the locale settings visible as GUC variables, too */
|
|
SetConfigOption("lc_collate", collate, PGC_INTERNAL, PGC_S_OVERRIDE);
|
|
SetConfigOption("lc_ctype", ctype, PGC_INTERNAL, PGC_S_OVERRIDE);
|
|
|
|
/* Use the right encoding in translated messages */
|
|
#ifdef ENABLE_NLS
|
|
pg_bind_textdomain_codeset(textdomain(NULL));
|
|
#endif
|
|
}
|
|
|
|
if (IS_THREAD_POOL_WORKER) {
|
|
// save for next session time restore.
|
|
SaveSessionEncodingInfo(db_form);
|
|
}
|
|
|
|
SetConfigOption("sql_compatibility", NameStr(db_form->datcompatibility), PGC_INTERNAL, PGC_S_OVERRIDE);
|
|
|
|
ReleaseSysCache(tup);
|
|
}
|
|
|
|
static void CheckConnAuthority(const char* name, bool am_superuser)
|
|
{
|
|
// Database Security: Check privilege to connect to the database.
|
|
// Only superuser on the local machine can connect to "template1".
|
|
if (IsUnderPostmaster && !IsAutoVacuumWorkerProcess() && !IsJobSchedulerProcess() && !IsJobWorkerProcess()) {
|
|
if ((IS_PGXC_COORDINATOR || IS_SINGLE_NODE) && IsConnFromApp() &&
|
|
(!am_superuser || !IsLocalAddr(u_sess->proc_cxt.MyProcPort)) &&
|
|
strcmp(name, "template1") == 0) {
|
|
ereport(FATAL,
|
|
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
|
|
errmsg("permission denied for database \"%s\"", name),
|
|
errdetail("User does not have CONNECT privilege.")));
|
|
}
|
|
}
|
|
}
|
|
|
|
/* --------------------------------
|
|
* InitCommunication
|
|
*
|
|
* This routine initializes stuff needed for ipc, locking, etc.
|
|
* it should be called something more informative.
|
|
* --------------------------------
|
|
*/
|
|
static void InitCommunication(void)
|
|
{
|
|
|
|
// initialize shared memory and semaphores appropriately.
|
|
if (!IsUnderPostmaster) { // postmaster already did this
|
|
/*
|
|
* We're running a postgres bootstrap process or a standalone backend.
|
|
* Create private "shmem" and semaphores.
|
|
*/
|
|
CreateSharedMemoryAndSemaphores(true, 0);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* pg_split_opts -- split a string of options and append it to an argv array
|
|
*
|
|
* NB: the input string is destructively modified! Also, caller is responsible
|
|
* for ensuring the argv array is large enough. The maximum possible number
|
|
* of arguments added by this routine is (strlen(optstr) + 1) / 2.
|
|
*
|
|
* Since no current POSTGRES arguments require any quoting characters,
|
|
* we can use the simple-minded tactic of assuming each set of space-
|
|
* delimited characters is a separate argv element.
|
|
*
|
|
* If you don't like that, well, we *used* to pass the whole option string
|
|
* as ONE argument to execl(), which was even less intelligent...
|
|
*/
|
|
void pg_split_opts(char** argv, int* argcp, char* optstr)
|
|
{
|
|
while (*optstr) {
|
|
while (isspace((unsigned char)*optstr)) {
|
|
optstr++;
|
|
}
|
|
if (*optstr == '\0') {
|
|
break;
|
|
}
|
|
|
|
argv[(*argcp)++] = optstr;
|
|
|
|
while (*optstr && !isspace((unsigned char)*optstr)) {
|
|
optstr++;
|
|
}
|
|
|
|
if (*optstr) {
|
|
*optstr++ = '\0';
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Early initialization of a backend (either standalone or under postmaster).
|
|
* This happens even before InitPostgres.
|
|
*
|
|
* This is separate from InitPostgres because it is also called by auxiliary
|
|
* processes, such as the background writer process, which may not call
|
|
* InitPostgres at all.
|
|
*/
|
|
void BaseInit(void)
|
|
{
|
|
/*
|
|
* Attach to shared memory and semaphores, and initialize our
|
|
* input/output/debugging file descriptors.
|
|
*/
|
|
InitCommunication();
|
|
DebugFileOpen();
|
|
|
|
/* Do local initialization of file, storage and buffer managers */
|
|
InitFileAccess();
|
|
smgrinit();
|
|
InitBufferPoolAccess();
|
|
}
|
|
|
|
/* -------------------------------------
|
|
* Postgres reset username and pgoption.
|
|
* -------------------------------------
|
|
*/
|
|
void PostgresResetUsernamePgoption(const char* username)
|
|
{
|
|
ereport(DEBUG3, (errmsg("PostgresResetUsernamePgoption()")));
|
|
|
|
bool boot_strap = IsBootstrapProcessingMode();
|
|
bool am_superuser = false;
|
|
|
|
/*
|
|
* 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 (!boot_strap && !dummyStandbyMode) {
|
|
/* 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.
|
|
*/
|
|
u_sess->utils_cxt.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.
|
|
*
|
|
* In standalone mode and in autovacuum worker processes, we use a fixed
|
|
* ID, otherwise we figure it out from the authenticated user name.
|
|
*/
|
|
if (boot_strap) {
|
|
InitializeSessionUserIdStandalone();
|
|
am_superuser = true;
|
|
} else if (!IsUnderPostmaster) {
|
|
InitializeSessionUserIdStandalone();
|
|
am_superuser = true;
|
|
|
|
if (!ThereIsAtLeastOneRole()) {
|
|
ereport(WARNING,
|
|
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
|
errmsg("no roles are defined in this database system"),
|
|
errhint("You should immediately run CREATE USER \"%s\" sysadmin;.", username)));
|
|
}
|
|
} else {
|
|
/* normal multiuser case */
|
|
Assert(u_sess->proc_cxt.MyProcPort != NULL);
|
|
|
|
if (AM_WAL_SENDER) {
|
|
InitializeSessionUserIdStandalone();
|
|
am_superuser = true;
|
|
} else {
|
|
/*
|
|
* In the wlm worker thread, we set the user is super user
|
|
* and the database is default database, we will send the query
|
|
* to data nodes with the user and the database.
|
|
*/
|
|
if (AmWLMWorkerProcess() || AmWLMMonitorProcess() || AmWLMArbiterProcess()) {
|
|
u_sess->proc_cxt.MyProcPort->user_name = (char*)GetSuperUserName((char*)username);
|
|
}
|
|
|
|
InitializeSessionUserId(username, InvalidOid);
|
|
am_superuser = superuser();
|
|
u_sess->misc_cxt.CurrentUserName = u_sess->proc_cxt.MyProcPort->user_name;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Now process any command-line switches and any additional GUC variable
|
|
* settings passed in the startup packet. We couldn't do this before
|
|
* because we didn't know if client is a superuser.
|
|
*/
|
|
if (u_sess->proc_cxt.MyProcPort != NULL) {
|
|
process_pgoptions(u_sess->proc_cxt.MyProcPort, am_superuser);
|
|
}
|
|
|
|
/* close the transaction we started above */
|
|
if (!boot_strap) {
|
|
CommitTransactionCommand();
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Process any command-line switches and any additional GUC variable
|
|
* settings passed in the startup packet.
|
|
*/
|
|
static void process_startup_options(Port* port, bool am_superuser)
|
|
{
|
|
GucContext gucctx;
|
|
ListCell* gucopts = NULL;
|
|
const int SEARCH_PATH_LEN = 64;
|
|
char sql[NAMEDATALEN + SEARCH_PATH_LEN] = {0};
|
|
int rc = -1;
|
|
char* name = NULL;
|
|
char* value = NULL;
|
|
|
|
gucctx = am_superuser ? PGC_SUSET : PGC_BACKEND;
|
|
|
|
/*
|
|
* First process any command-line switches that were included in the
|
|
* startup packet, if we are in a regular backend.
|
|
*/
|
|
if (port->cmdline_options != NULL) {
|
|
/*
|
|
* The maximum possible number of commandline arguments that could
|
|
* come from port->cmdline_options is (strlen + 1) / 2; see
|
|
* pg_split_opts().
|
|
*/
|
|
char** av;
|
|
int ac;
|
|
|
|
const int maxac = 2 + (strlen(port->cmdline_options) + 1) / 2;
|
|
|
|
av = (char**)palloc(maxac * sizeof(char*));
|
|
ac = 0;
|
|
|
|
av[ac++] = "postgres";
|
|
|
|
/* Note this mangles port->cmdline_options */
|
|
pg_split_opts(av, &ac, port->cmdline_options);
|
|
|
|
av[ac] = NULL;
|
|
|
|
Assert(ac < maxac);
|
|
|
|
(void)process_postgres_switches(ac, av, gucctx, NULL);
|
|
}
|
|
|
|
/*
|
|
* At this stage in session initialization, all system catalogs are accessable and
|
|
* we can try to load pgxc node information into shared memory if necessary.
|
|
*/
|
|
if (IS_PGXC_COORDINATOR && *t_thrd.pgxc_cxt.shmemNumCoordsInCluster == 0) {
|
|
PgxcNodeListAndCount();
|
|
}
|
|
|
|
/* sanity check for ha maintenance port -- only super users are allowed to connect with client applications. */
|
|
if (IsConnFromApp() && IsHAPort(port) && !am_superuser) {
|
|
ConnAuthMethodCorrect = false;
|
|
ereport(FATAL,
|
|
(errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION),
|
|
errmsg("Normal user is not allowed to use HA channel!")));
|
|
}
|
|
|
|
/* sanity check for peer address -- outer-cluster connections are only allowed from client applications */
|
|
if (IS_PGXC_COORDINATOR && !is_cluster_internal_connection(port) && !IsConnFromApp()) {
|
|
ereport(FATAL,
|
|
(errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION), errmsg("Only applications can connect remotely.")));
|
|
}
|
|
|
|
/* sanity check for inner maintenance tools */
|
|
if (u_sess->proc_cxt.IsInnerMaintenanceTools) {
|
|
/* check 1 -- forbid outer-cluster connections, except for resizing and replace with gs_ctl build */
|
|
if (((IS_PGXC_COORDINATOR && !is_cluster_internal_connection(port)) ||
|
|
(IS_SINGLE_NODE && !is_node_internal_connection(port))) &&
|
|
!(u_sess->proc_cxt.clientIsGsCtl && AM_WAL_SENDER)) {
|
|
ereport(FATAL,
|
|
(errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION),
|
|
errmsg("Forbid remote connection via internal maintenance tools.")));
|
|
}
|
|
|
|
/* check 2 -- forbid non-initial users, except during cluster resizing with gs_redis */
|
|
if (!dummyStandbyMode && GetRoleOid(port->user_name) != INITIAL_USER_ID &&
|
|
!(ClusterResizingInProgress() && u_sess->proc_cxt.clientIsGsredis)) {
|
|
ereport(FATAL,
|
|
(errcode(ERRCODE_INVALID_OPERATION), errmsg("Inner maintenance tools only for the initial user.")));
|
|
}
|
|
}
|
|
|
|
/* finally, inform alarm reporter about authentification result */
|
|
if (port->hba != NULL && !port->hba->remoteTrust && IsConnFromApp() && !CheckLocalConnection()) {
|
|
ConnAuthMethodCorrect = true;
|
|
}
|
|
|
|
/*
|
|
* Process any additional GUC variable settings passed in startup packet.
|
|
* These are handled exactly like command-line variables.
|
|
*/
|
|
gucopts = list_head(port->guc_options);
|
|
|
|
while (gucopts != NULL) {
|
|
name = (char*)lfirst(gucopts);
|
|
gucopts = lnext(gucopts);
|
|
|
|
value = (char*)lfirst(gucopts);
|
|
gucopts = lnext(gucopts);
|
|
|
|
SetConfigOption(name, value, gucctx, PGC_S_CLIENT);
|
|
/*
|
|
* JDBC can set schema with connect option,
|
|
* save it in pooler that need remote synchronous.
|
|
* WARNING: schema value do not case sensitive.
|
|
*/
|
|
if (IS_PGXC_COORDINATOR && !IsConnFromCoord() &&
|
|
((pg_strcasecmp(name, "search_path") == 0 &&
|
|
pg_strcasecmp(u_sess->attr.attr_common.namespace_search_path, value) == 0) ||
|
|
(pg_strcasecmp(name, "current_schema") == 0 &&
|
|
pg_strcasecmp(u_sess->attr.attr_common.namespace_current_schema, value) == 0))) {
|
|
rc = sprintf_s(sql, sizeof(sql), "SET %s = %s;", name, value);
|
|
securec_check_ss(rc, "\0", "\0");
|
|
(void)register_pooler_session_param(name, sql);
|
|
ereport(DEBUG1, (errmsg("Save pooler session param: %s in startup", sql)));
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Process pgoptions in pooler stateless reuse mode.
|
|
*/
|
|
static void process_pgoptions(Port* port, bool am_superuser)
|
|
{
|
|
GucContext gucctx;
|
|
|
|
gucctx = am_superuser ? PGC_SUSET : PGC_BACKEND;
|
|
|
|
/*
|
|
* Process any command-line if we are in a regular backend.
|
|
*/
|
|
if (port->cmdline_options != NULL) {
|
|
/*
|
|
* The maximum possible number of commandline arguments that could
|
|
* come from port->cmdline_options is (strlen + 1) / 2; see
|
|
* pg_split_opts().
|
|
*/
|
|
char** av;
|
|
int ac;
|
|
|
|
const int maxac = 2 + (strlen(port->cmdline_options) + 1) / 2;
|
|
|
|
av = (char**)palloc(maxac * sizeof(char*));
|
|
ac = 0;
|
|
|
|
av[ac++] = "postgres";
|
|
|
|
/* Note this mangles port->cmdline_options */
|
|
pg_split_opts(av, &ac, port->cmdline_options);
|
|
|
|
av[ac] = NULL;
|
|
|
|
Assert(ac < maxac);
|
|
|
|
(void)process_postgres_switches(ac, av, gucctx, NULL);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Load GUC settings from pg_db_role_setting.
|
|
*
|
|
* We try specific settings for the database/role combination, as well as
|
|
* general for this database and for this user.
|
|
*/
|
|
static void process_settings(Oid databaseid, Oid roleid)
|
|
{
|
|
Relation rel_setting;
|
|
|
|
if (!IsUnderPostmaster) {
|
|
return;
|
|
}
|
|
rel_setting = heap_open(DbRoleSettingRelationId, AccessShareLock);
|
|
|
|
/* Later settings are ignored if set earlier. */
|
|
ApplySetting(databaseid, roleid, rel_setting, PGC_S_DATABASE_USER);
|
|
ApplySetting(InvalidOid, roleid, rel_setting, PGC_S_USER);
|
|
ApplySetting(databaseid, InvalidOid, rel_setting, PGC_S_DATABASE);
|
|
|
|
heap_close(rel_setting, AccessShareLock);
|
|
}
|
|
|
|
/*
|
|
* Backend-shutdown callback. Do cleanup that we want to be sure happens
|
|
* before all the supporting modules begin to nail their doors shut via
|
|
* their own callbacks.
|
|
*
|
|
* User-level cleanup, such as temp-relation removal and UNLISTEN, happens
|
|
* via separate callbacks that execute before this one. We don't combine the
|
|
* callbacks because we still want this one to happen if the user-level
|
|
* cleanup fails.
|
|
*/
|
|
void ShutdownPostgres(int code, Datum arg)
|
|
{
|
|
SetInstrNull();
|
|
/* Mark recursive vfd is invalid before aborting transaction. */
|
|
#ifdef ENABLE_MULTIPLE_NODES
|
|
StreamNodeGroup::MarkRecursiveVfdInvalid();
|
|
#endif
|
|
/* Make sure we've killed any active transaction */
|
|
AbortOutOfAnyTransaction();
|
|
|
|
/*
|
|
* If stream Top consumer or stream thread end up as elog FATAL, we must wait until we
|
|
* get a sync point
|
|
*/
|
|
#ifdef ENABLE_MULTIPLE_NODES
|
|
StreamNodeGroup::syncQuit(STREAM_ERROR);
|
|
StreamNodeGroup::destroy(STREAM_ERROR);
|
|
ForgetRegisterStreamSnapshots();
|
|
#endif
|
|
/* Free remote xact state */
|
|
free_RemoteXactState();
|
|
|
|
/* If waiting, get off wait queue (should only be needed after error) */
|
|
LockErrorCleanup();
|
|
/* Release standard locks, including session-level if aborting */
|
|
LockReleaseAll(DEFAULT_LOCKMETHOD, true);
|
|
|
|
/*
|
|
* User locks are not released by transaction end, so be sure to release
|
|
* them explicitly.
|
|
*/
|
|
LockReleaseAll(USER_LOCKMETHOD, true);
|
|
}
|
|
|
|
/*
|
|
* Returns true if at least one role is defined in this database cluster.
|
|
*/
|
|
static bool ThereIsAtLeastOneRole(void)
|
|
{
|
|
Relation pg_authid_rel;
|
|
HeapScanDesc scan;
|
|
bool result = false;
|
|
|
|
pg_authid_rel = heap_open(AuthIdRelationId, AccessShareLock);
|
|
|
|
scan = heap_beginscan(pg_authid_rel, SnapshotNow, 0, NULL);
|
|
result = (heap_getnext(scan, ForwardScanDirection) != NULL);
|
|
|
|
heap_endscan(scan);
|
|
heap_close(pg_authid_rel, AccessShareLock);
|
|
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
* when initializing a Postgres-XC cluster node, it executes "CREATE
|
|
* NODE nodename WITH (type = 'coordinator');" to create node for the
|
|
* current node. The port is not given in this statement, so use the
|
|
* default value 5432. That is why we see the current node's port is 5432,
|
|
* no matter we modify the port or not before start up.
|
|
*
|
|
* This function is used to repair the port of current node in pgxc_node
|
|
* catalog. It is called at the initializing process of postgress, in order
|
|
* to repair the port only once, we use a mutex variable and a static variable.
|
|
*/
|
|
#ifdef PGXC
|
|
static void AlterPgxcNodePort(void)
|
|
{
|
|
const char* node_name = NULL;
|
|
const char* node_port_str = NULL;
|
|
int node_port;
|
|
HeapTuple oldtup, newtup;
|
|
Oid node_oid;
|
|
Relation rel;
|
|
Datum new_record[Natts_pgxc_node];
|
|
bool new_record_nulls[Natts_pgxc_node];
|
|
bool new_record_repl[Natts_pgxc_node];
|
|
static bool need_repair = TRUE;
|
|
volatile HaShmemData* hashmdata = t_thrd.postmaster_cxt.HaShmData;
|
|
char node_type = PGXC_NODE_NONE;
|
|
Form_pgxc_node pgxc_node_form;
|
|
|
|
SpinLockAcquire(&hashmdata->mutex);
|
|
if (hashmdata->current_mode == STANDBY_MODE || hashmdata->current_mode == PENDING_MODE) {
|
|
SpinLockRelease(&hashmdata->mutex);
|
|
return;
|
|
}
|
|
SpinLockRelease(&hashmdata->mutex);
|
|
|
|
if (!IsPostmasterEnvironment || !need_repair || isRestoreMode) {
|
|
return;
|
|
}
|
|
node_name = GetConfigOption("pgxc_node_name", false, false);
|
|
node_port_str = GetConfigOption("port", false, false);
|
|
node_oid = get_pgxc_nodeoid(node_name);
|
|
|
|
if (IS_PGXC_DATANODE) {
|
|
node_type = PGXC_NODE_DATANODE;
|
|
}
|
|
/* Only a DB administrator can alter cluster nodes */
|
|
if (!superuser()) {
|
|
return;
|
|
}
|
|
/* Look at the node tuple, and take exclusive lock on it */
|
|
rel = heap_open(PgxcNodeRelationId, RowExclusiveLock);
|
|
|
|
/* Check that node exists */
|
|
if (!OidIsValid(node_oid)) {
|
|
ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("PGXC Node %s: object not defined", node_name)));
|
|
}
|
|
/* Open new tuple, checks are performed on it and new values */
|
|
oldtup = SearchSysCacheCopy1(PGXCNODEOID, ObjectIdGetDatum(node_oid));
|
|
if (!HeapTupleIsValid(oldtup)) {
|
|
ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("cache lookup failed for object %u", node_oid)));
|
|
}
|
|
/* Update values for catalog entry */
|
|
node_port = atoi(node_port_str);
|
|
|
|
pgxc_node_form = (Form_pgxc_node)GETSTRUCT(oldtup);
|
|
|
|
if (pgxc_node_form->node_port != node_port || pgxc_node_form->node_port1 != node_port) {
|
|
int ss_rc = memset_s(new_record, sizeof(new_record), 0, sizeof(new_record));
|
|
securec_check(ss_rc, "\0", "\0");
|
|
ss_rc = memset_s(new_record_nulls, sizeof(new_record_nulls), false, sizeof(new_record_nulls));
|
|
securec_check(ss_rc, "\0", "\0");
|
|
ss_rc = memset_s(new_record_repl, sizeof(new_record_repl), false, sizeof(new_record_repl));
|
|
securec_check(ss_rc, "\0", "\0");
|
|
new_record[Anum_pgxc_node_port - 1] = Int32GetDatum(node_port);
|
|
new_record_repl[Anum_pgxc_node_port - 1] = true;
|
|
new_record[Anum_pgxc_node_port1 - 1] = Int32GetDatum(node_port);
|
|
new_record_repl[Anum_pgxc_node_port1 - 1] = true;
|
|
|
|
if (IS_PGXC_DATANODE) {
|
|
new_record[Anum_pgxc_node_type - 1] = CharGetDatum(node_type);
|
|
new_record_repl[Anum_pgxc_node_type - 1] = true;
|
|
}
|
|
|
|
/* Update relation */
|
|
newtup = heap_modify_tuple(oldtup, RelationGetDescr(rel), new_record, new_record_nulls, new_record_repl);
|
|
simple_heap_update(rel, &oldtup->t_self, newtup);
|
|
|
|
/* Update indexes */
|
|
CatalogUpdateIndexes(rel, newtup);
|
|
}
|
|
|
|
need_repair = FALSE;
|
|
|
|
heap_freetuple(oldtup);
|
|
/* Release lock at Commit */
|
|
heap_close(rel, NoLock);
|
|
}
|
|
#endif
|
|
|
|
PostgresInitializer::PostgresInitializer()
|
|
{
|
|
m_indbname = NULL;
|
|
m_dboid = InvalidOid;
|
|
m_username = NULL;
|
|
m_useroid = InvalidOid;
|
|
m_isSuperUser = false;
|
|
m_fullpath = NULL;
|
|
memset_s(m_dbname, NAMEDATALEN, 0, NAMEDATALEN);
|
|
memset_s(m_details, PGAUDIT_MAXLENGTH, 0, PGAUDIT_MAXLENGTH);
|
|
}
|
|
|
|
PostgresInitializer::~PostgresInitializer()
|
|
{
|
|
if (m_fullpath != NULL) {
|
|
pfree_ext(m_fullpath);
|
|
}
|
|
m_indbname = NULL;
|
|
m_username = NULL;
|
|
}
|
|
|
|
void PostgresInitializer::SetDatabaseAndUser(
|
|
const char* in_dbname, Oid dboid, const char* username, Oid useroid)
|
|
{
|
|
m_indbname = in_dbname;
|
|
m_dboid = dboid;
|
|
m_username = username;
|
|
m_useroid = useroid;
|
|
}
|
|
|
|
void PostgresInitializer::InitBootstrap()
|
|
{
|
|
InitThread();
|
|
|
|
InitSysCache();
|
|
|
|
SetProcessExitCallback();
|
|
|
|
SetSuperUserStandalone();
|
|
|
|
SetDefaultDatabase();
|
|
|
|
LoadSysCache();
|
|
|
|
ProcessStartupOpt();
|
|
|
|
InitPGXCPort();
|
|
|
|
InitSettings();
|
|
|
|
AuditUserLogin();
|
|
}
|
|
|
|
void PostgresInitializer::InitJobScheduler()
|
|
{
|
|
InitThread();
|
|
|
|
InitSysCache();
|
|
|
|
/* Initialize stats collection --- must happen before first xact */
|
|
pgstat_initialize();
|
|
|
|
SetProcessExitCallback();
|
|
|
|
StartXact();
|
|
|
|
SetSuperUserAndDatabase();
|
|
|
|
CheckConnPermission();
|
|
|
|
SetDatabase();
|
|
|
|
LoadSysCache();
|
|
|
|
CheckDatabaseAuth();
|
|
|
|
InitPGXCPort();
|
|
|
|
InitSettings();
|
|
|
|
FinishInit();
|
|
|
|
AuditUserLogin();
|
|
}
|
|
|
|
void PostgresInitializer::InitJobExecuteWorker()
|
|
{
|
|
InitThread();
|
|
|
|
InitSysCache();
|
|
|
|
/* Initialize stats collection --- must happen before first xact */
|
|
pgstat_initialize();
|
|
|
|
SetProcessExitCallback();
|
|
|
|
StartXact();
|
|
|
|
InitUser();
|
|
|
|
CheckConnPermission();
|
|
|
|
SetDatabase();
|
|
|
|
LoadSysCache();
|
|
|
|
CheckDatabaseAuth();
|
|
|
|
InitPGXCPort();
|
|
|
|
InitSettings();
|
|
|
|
FinishInit();
|
|
|
|
AuditUserLogin();
|
|
}
|
|
|
|
void PostgresInitializer::InitSnapshotWorker()
|
|
{
|
|
InitThread();
|
|
|
|
InitSysCache();
|
|
|
|
/* Initialize stats collection --- must happen before first xact */
|
|
pgstat_initialize();
|
|
|
|
SetProcessExitCallback();
|
|
|
|
StartXact();
|
|
|
|
SetSuperUserAndDatabase();
|
|
|
|
CheckConnPermission();
|
|
|
|
SetDatabase();
|
|
|
|
LoadSysCache();
|
|
|
|
CheckDatabaseAuth();
|
|
|
|
InitPGXCPort();
|
|
|
|
InitSettings();
|
|
|
|
FinishInit();
|
|
|
|
AuditUserLogin();
|
|
}
|
|
|
|
void PostgresInitializer::InitPercentileWorker()
|
|
{
|
|
InitThread();
|
|
|
|
InitSysCache();
|
|
|
|
/* Initialize stats collection --- must happen before first xact */
|
|
pgstat_initialize();
|
|
|
|
SetProcessExitCallback();
|
|
|
|
StartXact();
|
|
|
|
SetSuperUserAndDatabase();
|
|
|
|
CheckConnPermission();
|
|
|
|
SetDatabase();
|
|
|
|
LoadSysCache();
|
|
|
|
CheckDatabaseAuth();
|
|
|
|
InitPGXCPort();
|
|
|
|
InitSettings();
|
|
|
|
FinishInit();
|
|
|
|
AuditUserLogin();
|
|
}
|
|
|
|
void PostgresInitializer::InitAutoVacLauncher()
|
|
{
|
|
InitThread();
|
|
|
|
InitSysCache();
|
|
|
|
/* Initialize stats collection --- must happen before first xact */
|
|
pgstat_initialize();
|
|
|
|
SetProcessExitCallback();
|
|
|
|
return;
|
|
}
|
|
|
|
void PostgresInitializer::InitAutoVacWorker()
|
|
{
|
|
InitThread();
|
|
|
|
InitSysCache();
|
|
|
|
/* Initialize stats collection --- must happen before first xact */
|
|
pgstat_initialize();
|
|
|
|
SetProcessExitCallback();
|
|
|
|
StartXact();
|
|
|
|
SetSuperUserStandalone();
|
|
|
|
CheckConnPermission();
|
|
|
|
SetDatabase();
|
|
|
|
LoadSysCache();
|
|
|
|
CheckDatabaseAuth();
|
|
|
|
InitPGXCPort();
|
|
|
|
InitSettings();
|
|
|
|
FinishInit();
|
|
|
|
AuditUserLogin();
|
|
}
|
|
|
|
void PostgresInitializer::InitCatchupWorker()
|
|
{
|
|
InitThread();
|
|
|
|
InitSysCache();
|
|
|
|
/* Initialize stats collection --- must happen before first xact */
|
|
pgstat_initialize();
|
|
|
|
SetProcessExitCallback();
|
|
|
|
return;
|
|
}
|
|
|
|
void PostgresInitializer::InitBackendWorker()
|
|
{
|
|
InitThread();
|
|
|
|
pgstat_initialize();
|
|
|
|
SetProcessExitCallback();
|
|
|
|
if (!IS_THREAD_POOL_WORKER) {
|
|
InitSession();
|
|
} else {
|
|
pgstat_bestart();
|
|
pgstat_report_appname("ThreadPoolWorker");
|
|
pgstat_report_activity(STATE_IDLE, NULL);
|
|
}
|
|
}
|
|
|
|
void PostgresInitializer::InitStreamWorker()
|
|
{
|
|
InitThread();
|
|
|
|
InitSysCache();
|
|
|
|
/* Initialize stats collection --- must happen before first xact */
|
|
pgstat_initialize();
|
|
|
|
SetProcessExitCallback();
|
|
|
|
StartXact();
|
|
|
|
InitUser();
|
|
|
|
CheckConnPermission();
|
|
|
|
SetDatabase();
|
|
|
|
LoadSysCache();
|
|
|
|
CheckDatabaseAuth();
|
|
|
|
InitPGXCPort();
|
|
|
|
InitSettings();
|
|
|
|
FinishInit();
|
|
|
|
AuditUserLogin();
|
|
}
|
|
|
|
void PostgresInitializer::InitWLM()
|
|
{
|
|
InitThread();
|
|
|
|
InitSysCache();
|
|
|
|
/* Initialize stats collection --- must happen before first xact */
|
|
pgstat_initialize();
|
|
|
|
SetProcessExitCallback();
|
|
|
|
StartXact();
|
|
|
|
SetSuperUserAndDatabase();
|
|
|
|
CheckConnPermission();
|
|
|
|
SetDatabase();
|
|
|
|
LoadSysCache();
|
|
|
|
CheckDatabaseAuth();
|
|
|
|
InitPGXCPort();
|
|
|
|
InitSettings();
|
|
|
|
FinishInit();
|
|
|
|
AuditUserLogin();
|
|
}
|
|
|
|
void PostgresInitializer::InitWAL()
|
|
{
|
|
/* Check replication permissions needed for walsender processes. */
|
|
Assert(!IsBootstrapProcessingMode());
|
|
|
|
InitThread();
|
|
|
|
InitSysCache();
|
|
|
|
/* Initialize stats collection --- must happen before first xact */
|
|
pgstat_initialize();
|
|
|
|
SetProcessExitCallback();
|
|
|
|
StartXact();
|
|
|
|
CheckAuthentication();
|
|
|
|
/* Don't set superuser when connection is from gs_basebackup */
|
|
if (u_sess->proc_cxt.clientIsGsBasebackup) {
|
|
InitUser();
|
|
} else {
|
|
SetSuperUserStandalone();
|
|
}
|
|
|
|
CheckConnPermission();
|
|
|
|
if (!AM_WAL_DB_SENDER) {
|
|
InitPlainWalSender();
|
|
return;
|
|
}
|
|
|
|
SetDatabase();
|
|
|
|
LoadSysCache();
|
|
|
|
CheckDatabaseAuth();
|
|
|
|
InitPGXCPort();
|
|
|
|
InitSettings();
|
|
|
|
FinishInit();
|
|
|
|
AuditUserLogin();
|
|
}
|
|
|
|
void PostgresInitializer::GetDatabaseName(char* out_dbname)
|
|
{
|
|
/* pass the database name back to the caller */
|
|
if (out_dbname != NULL) {
|
|
errno_t rc = strcpy_s(out_dbname, NAMEDATALEN, m_dbname);
|
|
securec_check_c(rc, "\0", "\0");
|
|
}
|
|
}
|
|
|
|
void PostgresInitializer::InitThread()
|
|
{
|
|
ereport(DEBUG3, (errmsg("InitPostgres")));
|
|
/*
|
|
* 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 t_thrd.proc_cxt.MyBackendId, a unique backend identifier.
|
|
*/
|
|
t_thrd.proc_cxt.MyBackendId = InvalidBackendId;
|
|
|
|
SharedInvalBackendInit(IS_THREAD_POOL_WORKER, false);
|
|
|
|
if (t_thrd.proc_cxt.MyBackendId > g_instance.shmem_cxt.MaxBackends || t_thrd.proc_cxt.MyBackendId <= 0) {
|
|
ereport(FATAL, (errmsg("bad backend ID: %d", t_thrd.proc_cxt.MyBackendId)));
|
|
}
|
|
/* Now that we have a BackendId, we can participate in ProcSignal */
|
|
ProcSignalInit(t_thrd.proc_cxt.MyBackendId);
|
|
|
|
/*
|
|
* 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);
|
|
}
|
|
}
|
|
|
|
void PostgresInitializer::InitSession()
|
|
{
|
|
/* Init rel cache for new session. */
|
|
InitSysCache();
|
|
|
|
StartXact();
|
|
|
|
if (!IsUnderPostmaster) {
|
|
CheckAtLeastOneRoles();
|
|
SetSuperUserStandalone();
|
|
} else if (t_thrd.bgworker_cxt.is_background_worker) {
|
|
if (m_username == NULL && !OidIsValid(m_useroid)) {
|
|
InitializeSessionUserIdStandalone();
|
|
m_isSuperUser = true;
|
|
} else {
|
|
InitUser();
|
|
}
|
|
} else {
|
|
CheckAuthentication();
|
|
InitUser();
|
|
}
|
|
|
|
CheckConnPermission();
|
|
|
|
SetDatabase();
|
|
|
|
LoadSysCache();
|
|
|
|
CheckDatabaseAuth();
|
|
|
|
InitPGXCPort();
|
|
|
|
InitSettings();
|
|
|
|
FinishInit();
|
|
|
|
AuditUserLogin();
|
|
}
|
|
|
|
void PostgresInitializer::InitSysCache()
|
|
{
|
|
/*
|
|
* 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();
|
|
/*
|
|
* Load relcache entries for the shared system catalogs. This must create
|
|
* at least entries for pg_database and catalogs used for authentication.
|
|
*/
|
|
RelationCacheInitializePhase2();
|
|
|
|
PartitionCacheInitialize();
|
|
|
|
BucketCacheInitialize();
|
|
|
|
InitCatalogCache();
|
|
InitPlanCache();
|
|
|
|
#ifdef ENABLE_MULTIPLE_NODES
|
|
/* init pgxc caches (local and global) */
|
|
PGXC_Cache_Init();
|
|
#endif
|
|
|
|
/* Initialize portal manager */
|
|
EnablePortalManager();
|
|
}
|
|
|
|
void PostgresInitializer::SetProcessExitCallback()
|
|
{
|
|
/*
|
|
* 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);
|
|
}
|
|
|
|
void PostgresInitializer::StartXact()
|
|
{
|
|
/*
|
|
* 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 (!dummyStandbyMode) {
|
|
/* 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.
|
|
*/
|
|
u_sess->utils_cxt.XactIsoLevel = XACT_READ_COMMITTED;
|
|
|
|
(void)GetTransactionSnapshot();
|
|
}
|
|
}
|
|
|
|
void PostgresInitializer::CheckAuthentication()
|
|
{
|
|
/* for logic conn, we do auth in libcomm, so no auth process anymore */
|
|
if (u_sess->proc_cxt.MyProcPort->is_logic_conn) {
|
|
u_sess->ClientAuthInProgress = false;
|
|
} else {
|
|
PerformAuthentication(u_sess->proc_cxt.MyProcPort);
|
|
}
|
|
}
|
|
|
|
void PostgresInitializer::SetSuperUserStandalone()
|
|
{
|
|
InitializeSessionUserIdStandalone();
|
|
m_isSuperUser = true;
|
|
}
|
|
|
|
void PostgresInitializer::CheckAtLeastOneRoles()
|
|
{
|
|
if (!ThereIsAtLeastOneRole()) {
|
|
ereport(WARNING,
|
|
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
|
errmsg("no roles are defined in this database system"),
|
|
errhint("You should immediately run CREATE USER \"%s\" sysadmin;.", m_username)));
|
|
}
|
|
}
|
|
|
|
void PostgresInitializer::SetSuperUserAndDatabase()
|
|
{
|
|
/*
|
|
* In the wlm worker thread, we set the user is super user
|
|
* and the database is default database, we will send the query
|
|
* to data nodes with the user and the database.
|
|
*/
|
|
u_sess->proc_cxt.MyProcPort->database_name = (char*)m_indbname;
|
|
u_sess->proc_cxt.MyProcPort->user_name = (char*)GetSuperUserName((char*)m_username);
|
|
InitUser();
|
|
}
|
|
|
|
void PostgresInitializer::InitUser()
|
|
{
|
|
InitializeSessionUserId(m_username, m_useroid);
|
|
m_isSuperUser = superuser();
|
|
u_sess->misc_cxt.CurrentUserName = u_sess->proc_cxt.MyProcPort->user_name;
|
|
}
|
|
|
|
void PostgresInitializer::CheckConnPermission()
|
|
{
|
|
CheckConnPermissionInShutDown();
|
|
CheckConnPermissionInBinaryUpgrade();
|
|
CheckConnLimitation();
|
|
}
|
|
|
|
void PostgresInitializer::CheckConnPermissionInShutDown()
|
|
{
|
|
/*
|
|
* If we're trying to shut down, only superusers can connect, and new
|
|
* replication connections are not allowed.
|
|
*/
|
|
if ((!m_isSuperUser || AM_WAL_SENDER) && u_sess->proc_cxt.MyProcPort != NULL &&
|
|
u_sess->proc_cxt.MyProcPort->canAcceptConnections == CAC_WAITBACKUP) {
|
|
if (AM_WAL_SENDER) {
|
|
ereport(FATAL,
|
|
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
|
|
errmsg("new replication connections are not allowed during database shutdown")));
|
|
} else {
|
|
ereport(FATAL,
|
|
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
|
|
errmsg("must be system admin to connect during database shutdown")));
|
|
}
|
|
}
|
|
}
|
|
|
|
void PostgresInitializer::CheckConnPermissionInBinaryUpgrade()
|
|
{
|
|
/*
|
|
* Binary upgrades only allowed super-user connections
|
|
*/
|
|
if (u_sess->proc_cxt.IsBinaryUpgrade && !m_isSuperUser) {
|
|
ereport(FATAL,
|
|
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
|
|
errmsg("must be system admin to connect in binary upgrade mode")));
|
|
}
|
|
}
|
|
|
|
void PostgresInitializer::CheckConnLimitation()
|
|
{
|
|
/*
|
|
* The last few connections slots are reserved for superusers and CM agent.
|
|
* Although replication connections currently require superuser privileges,
|
|
* we don't allow them to consume the reserved slots, which are intended for
|
|
* interactive use.
|
|
* Inner tools use independent counter.
|
|
*/
|
|
if (!u_sess->proc_cxt.IsInnerMaintenanceTools) {
|
|
if (GetUsedConnectionCount() > g_instance.attr.attr_network.MaxConnections ||
|
|
!HaveNFreeProcs(g_instance.attr.attr_network.ReservedBackends)) {
|
|
/* if postgres for am_superuser, allowed to pass */
|
|
if (!m_isSuperUser || AM_WAL_SENDER) {
|
|
int active_count = pgstat_get_current_active_numbackends();
|
|
ereport(FATAL,
|
|
(errcode(ERRCODE_TOO_MANY_CONNECTIONS),
|
|
errmsg("Already too many clients, "
|
|
"active/non-active/reserved: %d/%d/%d.",
|
|
active_count,
|
|
GetUsedConnectionCount() - active_count,
|
|
g_instance.attr.attr_network.ReservedBackends)));
|
|
}
|
|
}
|
|
} else if (GetUsedInnerToolConnCount() > g_instance.attr.attr_network.maxInnerToolConnections) {
|
|
ereport(FATAL, (errcode(ERRCODE_TOO_MANY_CONNECTIONS), errmsg("Already too many tools connected, max num: %d",
|
|
g_instance.attr.attr_network.maxInnerToolConnections)));
|
|
}
|
|
}
|
|
|
|
void PostgresInitializer::InitPlainWalSender()
|
|
{
|
|
/*
|
|
* If this is a plain walsender only supporting physical replication, we
|
|
* don't want to connect to any particular database. Just finish the
|
|
* backend startup by processing any options from the startup packet, and
|
|
* we're done.
|
|
*/
|
|
/* process any options passed in the startup packet */
|
|
if (u_sess->proc_cxt.MyProcPort != NULL) {
|
|
process_startup_options(u_sess->proc_cxt.MyProcPort, m_isSuperUser);
|
|
}
|
|
/* Apply PostAuthDelay as soon as we've read all options */
|
|
if (u_sess->attr.attr_security.PostAuthDelay > 0) {
|
|
pg_usleep(u_sess->attr.attr_security.PostAuthDelay * 1000000L);
|
|
}
|
|
/* initialize client encoding */
|
|
InitializeClientEncoding();
|
|
|
|
/* report this backend in the PgBackendStatus array */
|
|
pgstat_bestart();
|
|
|
|
/* close the transaction we started above */
|
|
if (!dummyStandbyMode) {
|
|
CommitTransactionCommand();
|
|
}
|
|
}
|
|
|
|
void PostgresInitializer::SetDefaultDatabase()
|
|
{
|
|
u_sess->proc_cxt.MyDatabaseId = TemplateDbOid;
|
|
u_sess->proc_cxt.MyDatabaseTableSpace = DEFAULTTABLESPACE_OID;
|
|
t_thrd.proc->databaseId = u_sess->proc_cxt.MyDatabaseId;
|
|
|
|
m_fullpath = GetDatabasePath(u_sess->proc_cxt.MyDatabaseId, u_sess->proc_cxt.MyDatabaseTableSpace);
|
|
/* This should happen only once per process */
|
|
Assert(!u_sess->proc_cxt.DatabasePath);
|
|
u_sess->proc_cxt.DatabasePath = MemoryContextStrdup(u_sess->top_mem_cxt, m_fullpath);
|
|
}
|
|
|
|
void PostgresInitializer::SetDatabase()
|
|
{
|
|
/*
|
|
* Set up the global variables holding database id and default tablespace.
|
|
* But note we won't actually try to touch the database just yet.
|
|
*
|
|
* We take a shortcut in the bootstrap case, otherwise we have to look up
|
|
* the db's entry in pg_database.
|
|
*/
|
|
if (m_indbname != NULL) {
|
|
SetDatabaseByName();
|
|
} else {
|
|
SetDatabaseByOid();
|
|
}
|
|
LockDatabase();
|
|
|
|
RecheckDatabaseExists();
|
|
|
|
SetDatabasePath();
|
|
}
|
|
|
|
void PostgresInitializer::SetDatabaseByName()
|
|
{
|
|
HeapTuple tuple;
|
|
Form_pg_database db_form;
|
|
|
|
tuple = GetDatabaseTuple(m_indbname);
|
|
|
|
if (!HeapTupleIsValid(tuple)) {
|
|
/* Database Security: Support database audit */
|
|
errno_t rc = snprintf_s(m_details,
|
|
sizeof(m_details),
|
|
sizeof(m_details) - 1,
|
|
"login db failed,database(%s)does not exist",
|
|
m_indbname);
|
|
securec_check_ss(rc, "\0", "\0");
|
|
pgaudit_user_login(FALSE, (char*)m_indbname, m_details);
|
|
ereport(FATAL, (errcode(ERRCODE_UNDEFINED_DATABASE), errmsg("database \"%s\" does not exist", m_indbname)));
|
|
}
|
|
db_form = (Form_pg_database)GETSTRUCT(tuple);
|
|
u_sess->proc_cxt.MyDatabaseId = HeapTupleGetOid(tuple);
|
|
u_sess->proc_cxt.MyDatabaseTableSpace = db_form->dattablespace;
|
|
/* take database name from the caller, just for paranoia */
|
|
strlcpy(m_dbname, m_indbname, sizeof(m_dbname));
|
|
}
|
|
|
|
void PostgresInitializer::SetDatabaseByOid()
|
|
{
|
|
/* caller specified database by OID */
|
|
HeapTuple tuple;
|
|
Form_pg_database db_form;
|
|
|
|
tuple = GetDatabaseTupleByOid(m_dboid);
|
|
|
|
if (!HeapTupleIsValid(tuple)) {
|
|
// Database Security: Support database audit
|
|
// Audit user login
|
|
snprintf_s(
|
|
m_details, sizeof(m_details), sizeof(m_details) - 1, "login db failed,database(%u)does not exist", m_dboid);
|
|
pgaudit_user_login(FALSE, "unkown", m_details);
|
|
|
|
ereport(FATAL, (errcode(ERRCODE_UNDEFINED_DATABASE), errmsg("database %u does not exist", m_dboid)));
|
|
}
|
|
db_form = (Form_pg_database)GETSTRUCT(tuple);
|
|
u_sess->proc_cxt.MyDatabaseId = HeapTupleGetOid(tuple);
|
|
u_sess->proc_cxt.MyDatabaseTableSpace = db_form->dattablespace;
|
|
Assert(u_sess->proc_cxt.MyDatabaseId == m_dboid);
|
|
strlcpy(m_dbname, NameStr(db_form->datname), sizeof(m_dbname));
|
|
}
|
|
|
|
void PostgresInitializer::LockDatabase()
|
|
{
|
|
/*
|
|
* Now, take a writer's lock on the database we are trying to connect to.
|
|
* If there is a concurrently running DROP DATABASE on that database, this
|
|
* will block us until it finishes (and has committed its update of
|
|
* pg_database).
|
|
*
|
|
* Note that the lock is not held long, only until the end of this startup
|
|
* transaction. This is OK since we will advertise our use of the
|
|
* database in the ProcArray before dropping the lock (in fact, that's the
|
|
* next thing to do). Anyone trying a DROP DATABASE after this point will
|
|
* see us in the array once they have the lock. Ordering is important for
|
|
* this because we don't want to advertise ourselves as being in this
|
|
* database until we have the lock; otherwise we create what amounts to a
|
|
* deadlock with CountOtherDBBackends().
|
|
*
|
|
* Note: use of RowExclusiveLock here is reasonable because we envision
|
|
* our session as being a concurrent writer of the database. If we had a
|
|
* way of declaring a session as being guaranteed-read-only, we could use
|
|
* AccessShareLock for such sessions and thereby not conflict against
|
|
* CREATE DATABASE.
|
|
*/
|
|
LockSharedObject(DatabaseRelationId, u_sess->proc_cxt.MyDatabaseId, 0, RowExclusiveLock);
|
|
/*
|
|
* Now we can mark our PGPROC entry with the database ID.
|
|
*
|
|
* We assume this is an atomic store so no lock is needed; though actually
|
|
* things would work fine even if it weren't atomic. Anyone searching the
|
|
* ProcArray for this database's ID should hold the database lock, so they
|
|
* would not be executing concurrently with this store. A process looking
|
|
* for another database's ID could in theory see a chance match if it read
|
|
* a partially-updated databaseId value; but as long as all such searches
|
|
* wait and retry, as in CountOtherDBBackends(), they will certainly see
|
|
* the correct value on their next try.
|
|
*/
|
|
t_thrd.proc->databaseId = u_sess->proc_cxt.MyDatabaseId;
|
|
}
|
|
|
|
void PostgresInitializer::RecheckDatabaseExists()
|
|
{
|
|
/*
|
|
* Recheck pg_database to make sure the target database hasn't gone away.
|
|
* If there was a concurrent DROP DATABASE, this ensures we will die
|
|
* cleanly without creating a mess.
|
|
*/
|
|
HeapTuple tuple;
|
|
|
|
tuple = GetDatabaseTuple(m_dbname);
|
|
|
|
if (!HeapTupleIsValid(tuple) || u_sess->proc_cxt.MyDatabaseId != HeapTupleGetOid(tuple) ||
|
|
u_sess->proc_cxt.MyDatabaseTableSpace != ((Form_pg_database)GETSTRUCT(tuple))->dattablespace) {
|
|
// Database Security: Support database audit
|
|
// Audit user login
|
|
errno_t rc = snprintf_s(m_details,
|
|
sizeof(m_details),
|
|
sizeof(m_details) - 1,
|
|
"database \"%s\" does not exist,It seems to have just been dropped or renamed",
|
|
m_dbname);
|
|
securec_check_ss(rc, "\0", "\0");
|
|
pgaudit_user_login(FALSE, m_dbname, m_details);
|
|
ereport(FATAL,
|
|
(errcode(ERRCODE_UNDEFINED_DATABASE),
|
|
errmsg("database \"%s\" does not exist", m_dbname),
|
|
errdetail("It seems to have just been dropped or renamed.")));
|
|
}
|
|
}
|
|
|
|
void PostgresInitializer::SetDatabasePath()
|
|
{
|
|
/*
|
|
* Now we should be able to access the database directory safely. Verify
|
|
* it's there and looks reasonable.
|
|
*/
|
|
m_fullpath = GetDatabasePath(u_sess->proc_cxt.MyDatabaseId, u_sess->proc_cxt.MyDatabaseTableSpace);
|
|
|
|
if (access(m_fullpath, F_OK) == -1) {
|
|
// Database Security: Support database audit
|
|
// Audit login db
|
|
int rcs = snprintf_truncated_s(
|
|
m_details, sizeof(m_details), "Audit messge:login db(%s) failed, database not exists", m_dbname);
|
|
securec_check_ss(rcs, "\0", "\0");
|
|
|
|
pgaudit_user_login(FALSE, (char*)m_username, m_details);
|
|
if (errno == ENOENT) {
|
|
ereport(FATAL,
|
|
(errcode(ERRCODE_UNDEFINED_DATABASE),
|
|
errmsg("database \"%s\" does not exist", m_dbname),
|
|
errdetail("The database subdirectory \"%s\" is missing.", m_fullpath)));
|
|
} else {
|
|
ereport(FATAL, (errcode_for_file_access(), errmsg("could not access directory \"%s\": %m", m_fullpath)));
|
|
}
|
|
}
|
|
|
|
ValidatePgVersion(m_fullpath);
|
|
// This should happen only once per process
|
|
Assert(!u_sess->proc_cxt.DatabasePath);
|
|
u_sess->proc_cxt.DatabasePath = MemoryContextStrdup(u_sess->top_mem_cxt, m_fullpath);
|
|
}
|
|
|
|
void PostgresInitializer::LoadSysCache()
|
|
{
|
|
/*
|
|
* It's now possible to do real access to the system catalogs.
|
|
*
|
|
* Load relcache entries for the system catalogs. This must create at
|
|
* least the minimum set of "nailed-in" cache entries.
|
|
*/
|
|
RelationCacheInitializePhase3();
|
|
|
|
/* set up ACL framework (so CheckMyDatabase can check permissions) */
|
|
initialize_acl();
|
|
}
|
|
|
|
void PostgresInitializer::ProcessStartupOpt()
|
|
{
|
|
/*
|
|
* Now process any command-line switches and any additional GUC variable
|
|
* settings passed in the startup packet. We couldn't do this before
|
|
* because we didn't know if client is a superuser.
|
|
*/
|
|
if (u_sess->proc_cxt.MyProcPort != NULL) {
|
|
process_startup_options(u_sess->proc_cxt.MyProcPort, m_isSuperUser);
|
|
}
|
|
}
|
|
|
|
void PostgresInitializer::CheckDatabaseAuth()
|
|
{
|
|
/*
|
|
* Re-read the pg_database row for our database, check permissions and set
|
|
* up database-specific GUC settings. We can't do this until all the
|
|
* database-access infrastructure is up. (Also, it wants to know if the
|
|
* user is a superuser, so the above stuff has to happen first.)
|
|
*/
|
|
CheckMyDatabase(m_dbname, m_isSuperUser);
|
|
|
|
ProcessStartupOpt();
|
|
|
|
CheckConnAuthority(m_dbname, m_isSuperUser);
|
|
}
|
|
|
|
void PostgresInitializer::InitPGXCPort()
|
|
{
|
|
#ifndef ENABLE_MULTIPLE_NODES
|
|
/* don't need to init pgxc port for single node mode */
|
|
return;
|
|
#endif
|
|
|
|
#ifdef PGXC
|
|
/* update pgxc_node info from configfile */
|
|
LWLockAcquire(AlterPortLock, LW_EXCLUSIVE);
|
|
|
|
if (!u_sess->attr.attr_common.xc_maintenance_mode && !g_instance.attr.attr_storage.IsRoachStandbyCluster) {
|
|
AlterPgxcNodePort();
|
|
}
|
|
LWLockRelease(AlterPortLock);
|
|
#endif
|
|
}
|
|
|
|
void PostgresInitializer::InitSettings()
|
|
{
|
|
/* Process pg_db_role_setting options */
|
|
process_settings(u_sess->proc_cxt.MyDatabaseId, GetSessionUserId());
|
|
|
|
/* Apply PostAuthDelay as soon as we've read all options */
|
|
if (u_sess->attr.attr_security.PostAuthDelay > 0) {
|
|
pg_usleep(u_sess->attr.attr_security.PostAuthDelay * 1000000L);
|
|
}
|
|
/* set default namespace search path */
|
|
InitializeSearchPath();
|
|
|
|
/* initialize client encoding */
|
|
InitializeClientEncoding();
|
|
}
|
|
|
|
void PostgresInitializer::FinishInit()
|
|
{
|
|
/* report this backend in the PgBackendStatus array */
|
|
pgstat_bestart();
|
|
|
|
/*
|
|
* Create a global hashtable and list used for cluster sql count
|
|
* on processMemoryContext which could be shared among threads.
|
|
* And load all users into the hashtable and list at the same time.
|
|
*/
|
|
initSqlCount();
|
|
|
|
InitInstrWorkloadTransaction();
|
|
|
|
/* close the transaction we started above */
|
|
CommitTransactionCommand();
|
|
}
|
|
|
|
void PostgresInitializer::AuditUserLogin()
|
|
{
|
|
if (NULL != m_username) {
|
|
int rc = snprintf_s(m_details,
|
|
sizeof(m_details),
|
|
sizeof(m_details) - 1,
|
|
"login db(%s) success,the current user is:%s",
|
|
m_dbname,
|
|
m_username);
|
|
securec_check_ss(rc, "\0", "\0");
|
|
|
|
pgaudit_user_login(TRUE, m_dbname, m_details);
|
|
}
|
|
}
|