/* * ProcessRoleGUC -- * We now process pg_authid.rolconfig separately from InitializeSessionUserId, * since it's too early to access toast table before initializing all * relcaches in phase3. */ static void ProcessRoleGUC(void) { cqContext *pcqCtx; Oid roleId; HeapTuple roleTup; Datum datum; bool isnull; /* This should have been set by now */ roleId = GetUserId(); Assert(OidIsValid(roleId)); pcqCtx = caql_beginscan( NULL, cql("SELECT * FROM pg_authid " " WHERE oid = :1", ObjectIdGetDatum(roleId))); roleTup = caql_getnext(pcqCtx); if (!HeapTupleIsValid(roleTup)) ereport(FATAL, (errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION), errmsg("role %u does not exist", roleId), errSendAlert(false))); /* * Set up user-specific configuration variables. This is a good place to * do it so we don't have to read pg_authid twice during session startup. */ datum = caql_getattr(pcqCtx, Anum_pg_authid_rolconfig, &isnull); if (!isnull) { ArrayType *a = DatumGetArrayTypeP(datum); /* * We process all the options at SUSET level. We assume that the * right to insert an option into pg_authid was checked when it was * inserted. */ ProcessGUCArray(a, PGC_SUSET, PGC_S_USER, GUC_ACTION_SET); } caql_endscan(pcqCtx); }
/* * Scan pg_db_role_setting looking for applicable settings, and load them on * the current process. * * relsetting is pg_db_role_setting, already opened and locked. * * Note: we only consider setting for the exact databaseid/roleid combination. * This probably needs to be called more than once, with InvalidOid passed as * databaseid/roleid. */ void ApplySetting(Snapshot snapshot, Oid databaseid, Oid roleid, Relation relsetting, GucSource source) { SysScanDesc scan; ScanKeyData keys[2]; HeapTuple tup; ScanKeyInit(&keys[0], Anum_pg_db_role_setting_setdatabase, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(databaseid)); ScanKeyInit(&keys[1], Anum_pg_db_role_setting_setrole, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(roleid)); scan = systable_beginscan(relsetting, DbRoleSettingDatidRolidIndexId, true, snapshot, 2, keys); while (HeapTupleIsValid(tup = systable_getnext(scan))) { bool isnull; Datum datum; datum = heap_getattr(tup, Anum_pg_db_role_setting_setconfig, RelationGetDescr(relsetting), &isnull); if (!isnull) { ArrayType *a = DatumGetArrayTypeP(datum); /* * We process all the options at SUSET level. We assume that the * right to insert an option into pg_db_role_setting was checked * when it was inserted. */ ProcessGUCArray(a, PGC_SUSET, source, GUC_ACTION_SET); } } systable_endscan(scan); }
/* * 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 dbform; /* Fetch our pg_database row normally, via syscache */ tup = SearchSysCache(DATABASEOID, ObjectIdGetDatum(MyDatabaseId), 0, 0, 0); if (!HeapTupleIsValid(tup)) elog(ERROR, "cache lookup failed for database %u", MyDatabaseId); dbform = (Form_pg_database) GETSTRUCT(tup); /* This recheck is strictly paranoia */ if (strcmp(name, NameStr(dbform->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\".", MyDatabaseId, NameStr(dbform->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 && !IsAutoVacuumProcess()) { /* * Check that the database is currently allowing connections. */ if (!dbform->datallowconn) 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(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 (dbform->datconnlimit >= 0 && !am_superuser && CountDBBackends(MyDatabaseId) > dbform->datconnlimit) ereport(FATAL, (errcode(ERRCODE_TOO_MANY_CONNECTIONS), errmsg("too many connections for database \"%s\"", name))); } /* * OK, we're golden. Next to-do item is to save the encoding info out of * the pg_database tuple. */ SetDatabaseEncoding(dbform->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_DEFAULT); /* Use the right encoding in translated messages */ #ifdef ENABLE_NLS pg_bind_textdomain_codeset(textdomain(NULL)); #endif /* * Lastly, set up any database-specific configuration variables. */ if (IsUnderPostmaster) { Datum datum; bool isnull; datum = SysCacheGetAttr(DATABASEOID, tup, Anum_pg_database_datconfig, &isnull); if (!isnull) { ArrayType *a = DatumGetArrayTypeP(datum); ProcessGUCArray(a, PGC_S_DATABASE); } } ReleaseSysCache(tup); }
/* -------------------------------- * ReverifyMyDatabase * * Since we are forced to fetch the database OID out of pg_database without * benefit of locking or transaction ID checking (see utils/misc/database.c), * we might have gotten a wrong answer. Or, we might have attached to a * database that's in process of being destroyed by destroydb(). This * routine is called after we have all the locking and other infrastructure * running --- now we can check that we are really attached to a valid * database. * * In reality, if destroydb() is running in parallel with our startup, * it's pretty likely that we will have failed before now, due to being * unable to read some of the system tables within the doomed database. * This routine just exists to make *sure* we have not started up in an * invalid database. If we quit now, we should have managed to avoid * creating any serious problems. * * This is also a handy place to fetch the database encoding info out * of pg_database. * * To avoid having to read pg_database more times than necessary * during session startup, this place is also fitting to set up any * database-specific configuration variables. * -------------------------------- */ static void ReverifyMyDatabase(const char *name) { Relation pgdbrel; HeapScanDesc pgdbscan; ScanKeyData key; HeapTuple tup; Form_pg_database dbform; /* * Because we grab AccessShareLock here, we can be sure that destroydb * is not running in parallel with us (any more). */ pgdbrel = heap_openr(DatabaseRelationName, AccessShareLock); ScanKeyEntryInitialize(&key, 0, Anum_pg_database_datname, F_NAMEEQ, NameGetDatum(name)); pgdbscan = heap_beginscan(pgdbrel, SnapshotNow, 1, &key); tup = heap_getnext(pgdbscan, ForwardScanDirection); if (!HeapTupleIsValid(tup) || HeapTupleGetOid(tup) != MyDatabaseId) { /* OOPS */ heap_close(pgdbrel, AccessShareLock); /* * The only real problem I could have created is to load dirty * buffers for the dead database into shared buffer cache; if I * did, some other backend will eventually try to write them and * die in mdblindwrt. Flush any such pages to forestall trouble. */ DropBuffers(MyDatabaseId); /* Now I can commit hara-kiri with a clear conscience... */ ereport(FATAL, (errcode(ERRCODE_UNDEFINED_DATABASE), errmsg("database \"%s\", OID %u, has disappeared from pg_database", name, MyDatabaseId))); } /* * Also check that the database is currently allowing connections. * (We do not enforce this in standalone mode, however, so that there is * a way to recover from "UPDATE pg_database SET datallowconn = false;") */ dbform = (Form_pg_database) GETSTRUCT(tup); if (IsUnderPostmaster && !dbform->datallowconn) ereport(FATAL, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), errmsg("database \"%s\" is not currently accepting connections", name))); /* * OK, we're golden. Only other to-do item is to save the encoding * info out of the pg_database tuple. */ SetDatabaseEncoding(dbform->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_DEFAULT); /* * Set up database-specific configuration variables. */ if (IsUnderPostmaster) { Datum datum; bool isnull; datum = heap_getattr(tup, Anum_pg_database_datconfig, RelationGetDescr(pgdbrel), &isnull); if (!isnull) { ArrayType *a = DatumGetArrayTypeP(datum); ProcessGUCArray(a, PGC_S_DATABASE); } } heap_endscan(pgdbscan); heap_close(pgdbrel, AccessShareLock); }