aboutsummaryrefslogtreecommitdiff
path: root/src/backend/commands/dbcommands.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/commands/dbcommands.c')
-rw-r--r--src/backend/commands/dbcommands.c451
1 files changed, 240 insertions, 211 deletions
diff --git a/src/backend/commands/dbcommands.c b/src/backend/commands/dbcommands.c
index b21d750394d..518fed81968 100644
--- a/src/backend/commands/dbcommands.c
+++ b/src/backend/commands/dbcommands.c
@@ -3,19 +3,17 @@
* dbcommands.c
* Database management commands (create/drop database).
*
- * Note: database creation/destruction commands take ExclusiveLock on
- * pg_database to ensure that no two proceed in parallel. We must use
- * at least this level of locking to ensure that no two backends try to
- * write the flat-file copy of pg_database at once. We avoid using
- * AccessExclusiveLock since there's no need to lock out ordinary readers
- * of pg_database.
+ * Note: database creation/destruction commands use exclusive locks on
+ * the database objects (as expressed by LockSharedObject()) to avoid
+ * stepping on each others' toes. Formerly we used table-level locks
+ * on pg_database, but that's too coarse-grained.
*
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/commands/dbcommands.c,v 1.180 2006/05/03 22:45:26 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/commands/dbcommands.c,v 1.181 2006/05/04 16:07:29 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -53,7 +51,8 @@
/* non-export function prototypes */
-static bool get_db_info(const char *name, Oid *dbIdP, Oid *ownerIdP,
+static bool get_db_info(const char *name, LOCKMODE lockmode,
+ Oid *dbIdP, Oid *ownerIdP,
int *encodingP, bool *dbIsTemplateP, bool *dbAllowConnP,
Oid *dbLastSysOidP,
TransactionId *dbVacuumXidP, TransactionId *dbFrozenXidP,
@@ -80,13 +79,12 @@ createdb(const CreatedbStmt *stmt)
TransactionId src_frozenxid;
Oid src_deftablespace;
volatile Oid dst_deftablespace;
- volatile Relation pg_database_rel;
+ Relation pg_database_rel;
HeapTuple tuple;
- TupleDesc pg_database_dsc;
Datum new_record[Natts_pg_database];
char new_record_nulls[Natts_pg_database];
Oid dboid;
- volatile Oid datdba;
+ Oid datdba;
ListCell *option;
DefElem *dtablespacename = NULL;
DefElem *downer = NULL;
@@ -96,8 +94,8 @@ createdb(const CreatedbStmt *stmt)
char *dbname = stmt->dbname;
char *dbowner = NULL;
const char *dbtemplate = NULL;
- volatile int encoding = -1;
- volatile int dbconnlimit = -1;
+ int encoding = -1;
+ int dbconnlimit = -1;
/* don't call this in a transaction block */
PreventTransactionChain((void *) stmt, "CREATE DATABASE");
@@ -216,31 +214,25 @@ createdb(const CreatedbStmt *stmt)
check_is_member_of_role(GetUserId(), datdba);
/*
- * Check for db name conflict. There is a race condition here, since
- * another backend could create the same DB name before we commit.
- * However, holding an exclusive lock on pg_database for the whole time we
- * are copying the source database doesn't seem like a good idea, so
- * accept possibility of race to create. We will check again after we
- * grab the exclusive lock.
- */
- if (get_db_info(dbname, NULL, NULL, NULL,
- NULL, NULL, NULL, NULL, NULL, NULL))
- ereport(ERROR,
- (errcode(ERRCODE_DUPLICATE_DATABASE),
- errmsg("database \"%s\" already exists", dbname)));
-
- /*
- * Lookup database (template) to be cloned.
+ * Lookup database (template) to be cloned, and obtain share lock on it.
+ * ShareLock allows two CREATE DATABASEs to work from the same template
+ * concurrently, while ensuring no one is busy dropping it in parallel
+ * (which would be Very Bad since we'd likely get an incomplete copy
+ * without knowing it). This also prevents any new connections from being
+ * made to the source until we finish copying it, so we can be sure it
+ * won't change underneath us.
*/
if (!dbtemplate)
dbtemplate = "template1"; /* Default template database name */
- if (!get_db_info(dbtemplate, &src_dboid, &src_owner, &src_encoding,
+ if (!get_db_info(dbtemplate, ShareLock,
+ &src_dboid, &src_owner, &src_encoding,
&src_istemplate, &src_allowconn, &src_lastsysoid,
&src_vacuumxid, &src_frozenxid, &src_deftablespace))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_DATABASE),
- errmsg("template database \"%s\" does not exist", dbtemplate)));
+ errmsg("template database \"%s\" does not exist",
+ dbtemplate)));
/*
* Permission check: to copy a DB that's not marked datistemplate, you
@@ -258,8 +250,7 @@ createdb(const CreatedbStmt *stmt)
/*
* The source DB can't have any active backends, except this one
* (exception is to allow CREATE DB while connected to template1).
- * Otherwise we might copy inconsistent data. This check is not
- * bulletproof, since someone might connect while we are copying...
+ * Otherwise we might copy inconsistent data.
*/
if (DatabaseHasActiveBackends(src_dboid, true))
ereport(ERROR,
@@ -346,14 +337,65 @@ createdb(const CreatedbStmt *stmt)
src_vacuumxid = src_frozenxid = GetCurrentTransactionId();
/*
- * Preassign OID for pg_database tuple, so that we can compute db path. We
- * have to open pg_database to do this, but we don't want to take
- * ExclusiveLock yet, so just do it and close again.
+ * Check for db name conflict. This is just to give a more friendly
+ * error message than "unique index violation". There's a race condition
+ * but we're willing to accept the less friendly message in that case.
*/
- pg_database_rel = heap_open(DatabaseRelationId, AccessShareLock);
- dboid = GetNewOid(pg_database_rel);
- heap_close(pg_database_rel, AccessShareLock);
- pg_database_rel = NULL;
+ if (OidIsValid(get_database_oid(dbname)))
+ ereport(ERROR,
+ (errcode(ERRCODE_DUPLICATE_DATABASE),
+ errmsg("database \"%s\" already exists", dbname)));
+
+ /*
+ * Insert a new tuple into pg_database. This establishes our ownership
+ * of the new database name (anyone else trying to insert the same name
+ * will block on the unique index, and fail after we commit). It also
+ * assigns the OID that the new database will have.
+ */
+ pg_database_rel = heap_open(DatabaseRelationId, RowExclusiveLock);
+
+ /* Form tuple */
+ MemSet(new_record, 0, sizeof(new_record));
+ MemSet(new_record_nulls, ' ', sizeof(new_record_nulls));
+
+ new_record[Anum_pg_database_datname - 1] =
+ DirectFunctionCall1(namein, CStringGetDatum(dbname));
+ new_record[Anum_pg_database_datdba - 1] = ObjectIdGetDatum(datdba);
+ new_record[Anum_pg_database_encoding - 1] = Int32GetDatum(encoding);
+ new_record[Anum_pg_database_datistemplate - 1] = BoolGetDatum(false);
+ new_record[Anum_pg_database_datallowconn - 1] = BoolGetDatum(true);
+ new_record[Anum_pg_database_datconnlimit - 1] = Int32GetDatum(dbconnlimit);
+ new_record[Anum_pg_database_datlastsysoid - 1] = ObjectIdGetDatum(src_lastsysoid);
+ new_record[Anum_pg_database_datvacuumxid - 1] = TransactionIdGetDatum(src_vacuumxid);
+ new_record[Anum_pg_database_datfrozenxid - 1] = TransactionIdGetDatum(src_frozenxid);
+ new_record[Anum_pg_database_dattablespace - 1] = ObjectIdGetDatum(dst_deftablespace);
+
+ /*
+ * We deliberately set datconfig and datacl to defaults (NULL), rather
+ * than copying them from the template database. Copying datacl would
+ * be a bad idea when the owner is not the same as the template's
+ * owner. It's more debatable whether datconfig should be copied.
+ */
+ new_record_nulls[Anum_pg_database_datconfig - 1] = 'n';
+ new_record_nulls[Anum_pg_database_datacl - 1] = 'n';
+
+ tuple = heap_formtuple(RelationGetDescr(pg_database_rel),
+ new_record, new_record_nulls);
+
+ dboid = simple_heap_insert(pg_database_rel, tuple);
+
+ /* Update indexes */
+ CatalogUpdateIndexes(pg_database_rel, tuple);
+
+ /*
+ * Now generate additional catalog entries associated with the new DB
+ */
+
+ /* Register owner dependency */
+ recordDependencyOnOwner(DatabaseRelationId, dboid, datdba);
+
+ /* Create pg_shdepend entries for objects within database */
+ copyTemplateDependencies(src_dboid, dboid);
/*
* Force dirty buffers out to disk, to ensure source database is
@@ -435,64 +477,6 @@ createdb(const CreatedbStmt *stmt)
heap_close(rel, AccessShareLock);
/*
- * Now OK to grab exclusive lock on pg_database.
- */
- pg_database_rel = heap_open(DatabaseRelationId, ExclusiveLock);
-
- /* Check to see if someone else created same DB name meanwhile. */
- if (get_db_info(dbname, NULL, NULL, NULL,
- NULL, NULL, NULL, NULL, NULL, NULL))
- ereport(ERROR,
- (errcode(ERRCODE_DUPLICATE_DATABASE),
- errmsg("database \"%s\" already exists", dbname)));
-
- /*
- * Insert a new tuple into pg_database
- */
- pg_database_dsc = RelationGetDescr(pg_database_rel);
-
- /* Form tuple */
- MemSet(new_record, 0, sizeof(new_record));
- MemSet(new_record_nulls, ' ', sizeof(new_record_nulls));
-
- new_record[Anum_pg_database_datname - 1] =
- DirectFunctionCall1(namein, CStringGetDatum(dbname));
- new_record[Anum_pg_database_datdba - 1] = ObjectIdGetDatum(datdba);
- new_record[Anum_pg_database_encoding - 1] = Int32GetDatum(encoding);
- new_record[Anum_pg_database_datistemplate - 1] = BoolGetDatum(false);
- new_record[Anum_pg_database_datallowconn - 1] = BoolGetDatum(true);
- new_record[Anum_pg_database_datconnlimit - 1] = Int32GetDatum(dbconnlimit);
- new_record[Anum_pg_database_datlastsysoid - 1] = ObjectIdGetDatum(src_lastsysoid);
- new_record[Anum_pg_database_datvacuumxid - 1] = TransactionIdGetDatum(src_vacuumxid);
- new_record[Anum_pg_database_datfrozenxid - 1] = TransactionIdGetDatum(src_frozenxid);
- new_record[Anum_pg_database_dattablespace - 1] = ObjectIdGetDatum(dst_deftablespace);
-
- /*
- * We deliberately set datconfig and datacl to defaults (NULL), rather
- * than copying them from the template database. Copying datacl would
- * be a bad idea when the owner is not the same as the template's
- * owner. It's more debatable whether datconfig should be copied.
- */
- new_record_nulls[Anum_pg_database_datconfig - 1] = 'n';
- new_record_nulls[Anum_pg_database_datacl - 1] = 'n';
-
- tuple = heap_formtuple(pg_database_dsc, new_record, new_record_nulls);
-
- HeapTupleSetOid(tuple, dboid); /* override heap_insert's OID
- * selection */
-
- simple_heap_insert(pg_database_rel, tuple);
-
- /* Update indexes */
- CatalogUpdateIndexes(pg_database_rel, tuple);
-
- /* Register owner dependency */
- recordDependencyOnOwner(DatabaseRelationId, dboid, datdba);
-
- /* Create pg_shdepend entries for objects within database */
- copyTemplateDependencies(src_dboid, dboid);
-
- /*
* We force a checkpoint before committing. This effectively means
* that committed XLOG_DBASE_CREATE operations will never need to be
* replayed (at least not in ordinary crash recovery; we still have to
@@ -524,15 +508,21 @@ createdb(const CreatedbStmt *stmt)
RequestCheckpoint(true, false);
/*
+ * Close pg_database, but keep lock till commit (this is important
+ * to prevent any risk of deadlock failure while updating flat file)
+ */
+ heap_close(pg_database_rel, NoLock);
+
+ /*
* Set flag to update flat database file at commit.
*/
database_file_update_needed();
}
PG_CATCH();
{
- /* Don't hold pg_database lock while doing recursive remove */
- if (pg_database_rel != NULL)
- heap_close(pg_database_rel, ExclusiveLock);
+ /* Release lock on source database before doing recursive remove */
+ UnlockSharedObject(DatabaseRelationId, src_dboid, 0,
+ ShareLock);
/* Throw away any successfully copied subdirectories */
remove_dbtablespaces(dboid);
@@ -540,10 +530,6 @@ createdb(const CreatedbStmt *stmt)
PG_RE_THROW();
}
PG_END_TRY();
-
- /* Close pg_database, but keep exclusive lock till commit */
- /* This has to be outside the PG_TRY */
- heap_close(pg_database_rel, NoLock);
}
@@ -568,20 +554,15 @@ dropdb(const char *dbname, bool missing_ok)
errmsg("cannot drop the currently open database")));
/*
- * Obtain exclusive lock on pg_database. We need this to ensure that no
- * new backend starts up in the target database while we are deleting it.
- * (Actually, a new backend might still manage to start up, because it
- * isn't able to lock pg_database while starting. But it will detect its
- * error in ReverifyMyDatabase and shut down before any serious damage is
- * done. See postinit.c.)
- *
- * An ExclusiveLock, rather than AccessExclusiveLock, is sufficient since
- * ReverifyMyDatabase takes RowShareLock. This allows ordinary readers of
- * pg_database to proceed in parallel.
+ * Look up the target database's OID, and get exclusive lock on it.
+ * We need this to ensure that no new backend starts up in the target
+ * database while we are deleting it (see postinit.c), and that no one is
+ * using it as a CREATE DATABASE template or trying to delete it for
+ * themselves.
*/
- pgdbrel = heap_open(DatabaseRelationId, ExclusiveLock);
+ pgdbrel = heap_open(DatabaseRelationId, RowExclusiveLock);
- if (!get_db_info(dbname, &db_id, NULL, NULL,
+ if (!get_db_info(dbname, AccessExclusiveLock, &db_id, NULL, NULL,
&db_istemplate, NULL, NULL, NULL, NULL, NULL))
{
if (!missing_ok)
@@ -592,17 +573,18 @@ dropdb(const char *dbname, bool missing_ok)
}
else
{
-
/* Close pg_database, release the lock, since we changed nothing */
- heap_close(pgdbrel, ExclusiveLock);
+ heap_close(pgdbrel, RowExclusiveLock);
ereport(NOTICE,
(errmsg("database \"%s\" does not exist, skipping",
dbname)));
-
return;
}
}
+ /*
+ * Permission checks
+ */
if (!pg_database_ownercheck(db_id, GetUserId()))
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_DATABASE,
dbname);
@@ -618,7 +600,8 @@ dropdb(const char *dbname, bool missing_ok)
errmsg("cannot drop a template database")));
/*
- * Check for active backends in the target database.
+ * Check for active backends in the target database. (Because we hold
+ * the database lock, no new ones can start after this.)
*/
if (DatabaseHasActiveBackends(db_id, false))
ereport(ERROR,
@@ -640,8 +623,7 @@ dropdb(const char *dbname, bool missing_ok)
ReleaseSysCache(tup);
/*
- * Delete any comments associated with the database
- *
+ * Delete any comments associated with the database.
*/
DeleteSharedComments(db_id, DatabaseRelationId);
@@ -675,7 +657,10 @@ dropdb(const char *dbname, bool missing_ok)
*/
remove_dbtablespaces(db_id);
- /* Close pg_database, but keep exclusive lock till commit */
+ /*
+ * Close pg_database, but keep lock till commit (this is important
+ * to prevent any risk of deadlock failure while updating flat file)
+ */
heap_close(pgdbrel, NoLock);
/*
@@ -691,29 +676,18 @@ dropdb(const char *dbname, bool missing_ok)
void
RenameDatabase(const char *oldname, const char *newname)
{
- HeapTuple tup,
- newtup;
+ Oid db_id;
+ HeapTuple newtup;
Relation rel;
- SysScanDesc scan,
- scan2;
- ScanKeyData key,
- key2;
/*
- * Obtain ExclusiveLock so that no new session gets started while the
- * rename is in progress.
+ * Look up the target database's OID, and get exclusive lock on it.
+ * We need this for the same reasons as DROP DATABASE.
*/
- rel = heap_open(DatabaseRelationId, ExclusiveLock);
-
- ScanKeyInit(&key,
- Anum_pg_database_datname,
- BTEqualStrategyNumber, F_NAMEEQ,
- NameGetDatum(oldname));
- scan = systable_beginscan(rel, DatabaseNameIndexId, true,
- SnapshotNow, 1, &key);
+ rel = heap_open(DatabaseRelationId, RowExclusiveLock);
- tup = systable_getnext(scan);
- if (!HeapTupleIsValid(tup))
+ if (!get_db_info(oldname, AccessExclusiveLock, &db_id, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_DATABASE),
errmsg("database \"%s\" does not exist", oldname)));
@@ -724,36 +698,29 @@ RenameDatabase(const char *oldname, const char *newname)
* be an actual problem besides a little confusion, so think about this
* and decide.
*/
- if (HeapTupleGetOid(tup) == MyDatabaseId)
+ if (db_id == MyDatabaseId)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("current database may not be renamed")));
/*
- * Make sure the database does not have active sessions. Might not be
- * necessary, but it's consistent with other database operations.
+ * Make sure the database does not have active sessions. This is the
+ * same concern as above, but applied to other sessions.
*/
- if (DatabaseHasActiveBackends(HeapTupleGetOid(tup), false))
+ if (DatabaseHasActiveBackends(db_id, false))
ereport(ERROR,
(errcode(ERRCODE_OBJECT_IN_USE),
errmsg("database \"%s\" is being accessed by other users",
oldname)));
/* make sure the new name doesn't exist */
- ScanKeyInit(&key2,
- Anum_pg_database_datname,
- BTEqualStrategyNumber, F_NAMEEQ,
- NameGetDatum(newname));
- scan2 = systable_beginscan(rel, DatabaseNameIndexId, true,
- SnapshotNow, 1, &key2);
- if (HeapTupleIsValid(systable_getnext(scan2)))
+ if (OidIsValid(get_database_oid(newname)))
ereport(ERROR,
(errcode(ERRCODE_DUPLICATE_DATABASE),
errmsg("database \"%s\" already exists", newname)));
- systable_endscan(scan2);
/* must be owner */
- if (!pg_database_ownercheck(HeapTupleGetOid(tup), GetUserId()))
+ if (!pg_database_ownercheck(db_id, GetUserId()))
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_DATABASE,
oldname);
@@ -764,14 +731,19 @@ RenameDatabase(const char *oldname, const char *newname)
errmsg("permission denied to rename database")));
/* rename */
- newtup = heap_copytuple(tup);
+ newtup = SearchSysCacheCopy(DATABASEOID,
+ ObjectIdGetDatum(db_id),
+ 0, 0, 0);
+ if (!HeapTupleIsValid(newtup))
+ elog(ERROR, "cache lookup failed for database %u", db_id);
namestrcpy(&(((Form_pg_database) GETSTRUCT(newtup))->datname), newname);
simple_heap_update(rel, &newtup->t_self, newtup);
CatalogUpdateIndexes(rel, newtup);
- systable_endscan(scan);
-
- /* Close pg_database, but keep exclusive lock till commit */
+ /*
+ * Close pg_database, but keep lock till commit (this is important
+ * to prevent any risk of deadlock failure while updating flat file)
+ */
heap_close(rel, NoLock);
/*
@@ -821,7 +793,9 @@ AlterDatabase(AlterDatabaseStmt *stmt)
connlimit = intVal(dconnlimit->arg);
/*
- * We don't need ExclusiveLock since we aren't updating the flat file.
+ * Get the old tuple. We don't need a lock on the database per se,
+ * because we're not going to do anything that would mess up incoming
+ * connections.
*/
rel = heap_open(DatabaseRelationId, RowExclusiveLock);
ScanKeyInit(&scankey,
@@ -891,7 +865,9 @@ AlterDatabaseSet(AlterDatabaseSetStmt *stmt)
valuestr = flatten_set_variable_args(stmt->variable, stmt->value);
/*
- * We don't need ExclusiveLock since we aren't updating the flat file.
+ * Get the old tuple. We don't need a lock on the database per se,
+ * because we're not going to do anything that would mess up incoming
+ * connections.
*/
rel = heap_open(DatabaseRelationId, RowExclusiveLock);
ScanKeyInit(&scankey,
@@ -974,7 +950,9 @@ AlterDatabaseOwner(const char *dbname, Oid newOwnerId)
Form_pg_database datForm;
/*
- * We don't need ExclusiveLock since we aren't updating the flat file.
+ * Get the old tuple. We don't need a lock on the database per se,
+ * because we're not going to do anything that would mess up incoming
+ * connections.
*/
rel = heap_open(DatabaseRelationId, RowExclusiveLock);
ScanKeyInit(&scankey,
@@ -1077,72 +1055,127 @@ AlterDatabaseOwner(const char *dbname, Oid newOwnerId)
* Helper functions
*/
+/*
+ * Look up info about the database named "name". If the database exists,
+ * obtain the specified lock type on it, fill in any of the remaining
+ * parameters that aren't NULL, and return TRUE. If no such database,
+ * return FALSE.
+ */
static bool
-get_db_info(const char *name, Oid *dbIdP, Oid *ownerIdP,
+get_db_info(const char *name, LOCKMODE lockmode,
+ Oid *dbIdP, Oid *ownerIdP,
int *encodingP, bool *dbIsTemplateP, bool *dbAllowConnP,
Oid *dbLastSysOidP,
TransactionId *dbVacuumXidP, TransactionId *dbFrozenXidP,
Oid *dbTablespace)
{
+ bool result = false;
Relation relation;
- ScanKeyData scanKey;
- SysScanDesc scan;
- HeapTuple tuple;
- bool gottuple;
AssertArg(name);
/* Caller may wish to grab a better lock on pg_database beforehand... */
relation = heap_open(DatabaseRelationId, AccessShareLock);
- ScanKeyInit(&scanKey,
- Anum_pg_database_datname,
- BTEqualStrategyNumber, F_NAMEEQ,
- NameGetDatum(name));
+ /*
+ * Loop covers the rare case where the database is renamed before we
+ * can lock it. We try again just in case we can find a new one of
+ * the same name.
+ */
+ for (;;)
+ {
+ ScanKeyData scanKey;
+ SysScanDesc scan;
+ HeapTuple tuple;
+ Oid dbOid;
- scan = systable_beginscan(relation, DatabaseNameIndexId, true,
- SnapshotNow, 1, &scanKey);
+ /*
+ * there's no syscache for database-indexed-by-name,
+ * so must do it the hard way
+ */
+ ScanKeyInit(&scanKey,
+ Anum_pg_database_datname,
+ BTEqualStrategyNumber, F_NAMEEQ,
+ NameGetDatum(name));
- tuple = systable_getnext(scan);
+ scan = systable_beginscan(relation, DatabaseNameIndexId, true,
+ SnapshotNow, 1, &scanKey);
- gottuple = HeapTupleIsValid(tuple);
- if (gottuple)
- {
- Form_pg_database dbform = (Form_pg_database) GETSTRUCT(tuple);
-
- /* oid of the database */
- if (dbIdP)
- *dbIdP = HeapTupleGetOid(tuple);
- /* oid of the owner */
- if (ownerIdP)
- *ownerIdP = dbform->datdba;
- /* character encoding */
- if (encodingP)
- *encodingP = dbform->encoding;
- /* allowed as template? */
- if (dbIsTemplateP)
- *dbIsTemplateP = dbform->datistemplate;
- /* allowing connections? */
- if (dbAllowConnP)
- *dbAllowConnP = dbform->datallowconn;
- /* last system OID used in database */
- if (dbLastSysOidP)
- *dbLastSysOidP = dbform->datlastsysoid;
- /* limit of vacuumed XIDs */
- if (dbVacuumXidP)
- *dbVacuumXidP = dbform->datvacuumxid;
- /* limit of frozen XIDs */
- if (dbFrozenXidP)
- *dbFrozenXidP = dbform->datfrozenxid;
- /* default tablespace for this database */
- if (dbTablespace)
- *dbTablespace = dbform->dattablespace;
+ tuple = systable_getnext(scan);
+
+ if (!HeapTupleIsValid(tuple))
+ {
+ /* definitely no database of that name */
+ systable_endscan(scan);
+ break;
+ }
+
+ dbOid = HeapTupleGetOid(tuple);
+
+ systable_endscan(scan);
+
+ /*
+ * Now that we have a database OID, we can try to lock the DB.
+ */
+ if (lockmode != NoLock)
+ LockSharedObject(DatabaseRelationId, dbOid, 0, lockmode);
+
+ /*
+ * And now, re-fetch the tuple by OID. If it's still there and
+ * still the same name, we win; else, drop the lock and loop
+ * back to try again.
+ */
+ tuple = SearchSysCache(DATABASEOID,
+ ObjectIdGetDatum(dbOid),
+ 0, 0, 0);
+ if (HeapTupleIsValid(tuple))
+ {
+ Form_pg_database dbform = (Form_pg_database) GETSTRUCT(tuple);
+
+ if (strcmp(name, NameStr(dbform->datname)) == 0)
+ {
+ /* oid of the database */
+ if (dbIdP)
+ *dbIdP = dbOid;
+ /* oid of the owner */
+ if (ownerIdP)
+ *ownerIdP = dbform->datdba;
+ /* character encoding */
+ if (encodingP)
+ *encodingP = dbform->encoding;
+ /* allowed as template? */
+ if (dbIsTemplateP)
+ *dbIsTemplateP = dbform->datistemplate;
+ /* allowing connections? */
+ if (dbAllowConnP)
+ *dbAllowConnP = dbform->datallowconn;
+ /* last system OID used in database */
+ if (dbLastSysOidP)
+ *dbLastSysOidP = dbform->datlastsysoid;
+ /* limit of vacuumed XIDs */
+ if (dbVacuumXidP)
+ *dbVacuumXidP = dbform->datvacuumxid;
+ /* limit of frozen XIDs */
+ if (dbFrozenXidP)
+ *dbFrozenXidP = dbform->datfrozenxid;
+ /* default tablespace for this database */
+ if (dbTablespace)
+ *dbTablespace = dbform->dattablespace;
+ ReleaseSysCache(tuple);
+ result = true;
+ break;
+ }
+ /* can only get here if it was just renamed */
+ ReleaseSysCache(tuple);
+ }
+
+ if (lockmode != NoLock)
+ UnlockSharedObject(DatabaseRelationId, dbOid, 0, lockmode);
}
- systable_endscan(scan);
heap_close(relation, AccessShareLock);
- return gottuple;
+ return result;
}
/* Check if current user has createdb privileges */
@@ -1234,8 +1267,6 @@ remove_dbtablespaces(Oid db_id)
* get_database_oid - given a database name, look up the OID
*
* Returns InvalidOid if database name not found.
- *
- * This is not actually used in this file, but is exported for use elsewhere.
*/
Oid
get_database_oid(const char *dbname)
@@ -1277,8 +1308,6 @@ get_database_oid(const char *dbname)
* get_database_name - given a database OID, look up the name
*
* Returns a palloc'd string, or NULL if no such database.
- *
- * This is not actually used in this file, but is exported for use elsewhere.
*/
char *
get_database_name(Oid dbid)