📄 dbcommands.c
字号:
/* * 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 * make the XLOG entry for the benefit of PITR operations). This * avoids two nasty scenarios: * * #1: When PITR is off, we don't XLOG the contents of newly created * indexes; therefore the drop-and-recreate-whole-directory behavior * of DBASE_CREATE replay would lose such indexes. * * #2: Since we have to recopy the source database during DBASE_CREATE * replay, we run the risk of copying changes in it that were * committed after the original CREATE DATABASE command but before the * system crash that led to the replay. This is at least unexpected * and at worst could lead to inconsistencies, eg duplicate table * names. * * (Both of these were real bugs in releases 8.0 through 8.0.3.) * * In PITR replay, the first of these isn't an issue, and the second * is only a risk if the CREATE DATABASE and subsequent template * database change both occur while a base backup is being taken. * There doesn't seem to be much we can do about that except document * it as a limitation. * * Perhaps if we ever implement CREATE DATABASE in a less cheesy way, * we can avoid this. */ RequestCheckpoint(true, false); /* * 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); /* Throw away any successfully copied subdirectories */ remove_dbtablespaces(dboid); 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);}/* * DROP DATABASE */voiddropdb(const char *dbname){ Oid db_id; bool db_istemplate; Relation pgdbrel; SysScanDesc pgdbscan; ScanKeyData key; HeapTuple tup; PreventTransactionChain((void *) dbname, "DROP DATABASE"); AssertArg(dbname); if (strcmp(dbname, get_database_name(MyDatabaseId)) == 0) ereport(ERROR, (errcode(ERRCODE_OBJECT_IN_USE), 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. */ pgdbrel = heap_open(DatabaseRelationId, ExclusiveLock); if (!get_db_info(dbname, &db_id, NULL, NULL, &db_istemplate, NULL, NULL, NULL, NULL, NULL)) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_DATABASE), errmsg("database \"%s\" does not exist", dbname))); if (!pg_database_ownercheck(db_id, GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_DATABASE, dbname); /* * Disallow dropping a DB that is marked istemplate. This is just to * prevent people from accidentally dropping template0 or template1; they * can do so if they're really determined ... */ if (db_istemplate) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("cannot drop a template database"))); /* * Check for active backends in the target database. */ if (DatabaseHasActiveBackends(db_id, false)) ereport(ERROR, (errcode(ERRCODE_OBJECT_IN_USE), errmsg("database \"%s\" is being accessed by other users", dbname))); /* * Find the database's tuple by OID (should be unique). */ ScanKeyInit(&key, ObjectIdAttributeNumber, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(db_id)); pgdbscan = systable_beginscan(pgdbrel, DatabaseOidIndexId, true, SnapshotNow, 1, &key); tup = systable_getnext(pgdbscan); if (!HeapTupleIsValid(tup)) { /* * This error should never come up since the existence of the database * is checked earlier */ elog(ERROR, "database \"%s\" doesn't exist despite earlier reports to the contrary", dbname); } /* Remove the database's tuple from pg_database */ simple_heap_delete(pgdbrel, &tup->t_self); systable_endscan(pgdbscan); /* * Delete any comments associated with the database * * NOTE: this is probably dead code since any such comments should have * been in that database, not mine. */ DeleteComments(db_id, DatabaseRelationId, 0); /* * Remove shared dependency references for the database. */ dropDatabaseDependencies(db_id); /* * Drop pages for this database that are in the shared buffer cache. This * is important to ensure that no remaining backend tries to write out a * dirty buffer to the dead database later... */ DropBuffers(db_id); /* * Also, clean out any entries in the shared free space map. */ FreeSpaceMapForgetDatabase(db_id); /* * On Windows, force a checkpoint so that the bgwriter doesn't hold any * open files, which would cause rmdir() to fail. */#ifdef WIN32 RequestCheckpoint(true, false);#endif /* * Remove all tablespace subdirs belonging to the database. */ remove_dbtablespaces(db_id); /* Close pg_database, but keep exclusive lock till commit */ heap_close(pgdbrel, NoLock); /* * Set flag to update flat database file at commit. */ database_file_update_needed();}/* * Rename database */voidRenameDatabase(const char *oldname, const char *newname){ HeapTuple tup, newtup; Relation rel; SysScanDesc scan, scan2; ScanKeyData key, key2; /* * Obtain ExclusiveLock so that no new session gets started while the * rename is in progress. */ 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); tup = systable_getnext(scan); if (!HeapTupleIsValid(tup)) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_DATABASE), errmsg("database \"%s\" does not exist", oldname))); /* * XXX Client applications probably store the current database somewhere, * so renaming it could cause confusion. On the other hand, there may not * be an actual problem besides a little confusion, so think about this * and decide. */ if (HeapTupleGetOid(tup) == 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. */ if (DatabaseHasActiveBackends(HeapTupleGetOid(tup), 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))) 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())) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_DATABASE, oldname); /* must have createdb rights */ if (!have_createdb_privilege()) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("permission denied to rename database"))); /* rename */ newtup = heap_copytuple(tup); 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 */ heap_close(rel, NoLock); /* * Set flag to update flat database file at commit. */ database_file_update_needed();}/* * ALTER DATABASE name ... */voidAlterDatabase(AlterDatabaseStmt *stmt){ Relation rel; HeapTuple tuple, newtuple; ScanKeyData scankey; SysScanDesc scan; ListCell *option; int connlimit = -1; DefElem *dconnlimit = NULL; Datum new_record[Natts_pg_database]; char new_record_nulls[Natts_pg_database]; char new_record_repl[Natts_pg_database]; /* Extract options from the statement node tree */ foreach(option, stmt->options) { DefElem *defel = (DefElem *) lfirst(option); if (strcmp(defel->defname, "connectionlimit") == 0) { if (dconnlimit) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("conflicting or redundant options"))); dconnlimit = defel; } else elog(ERROR, "option \"%s\" not recognized", defel->defname); } if (dconnlimit) connlimit = intVal(dconnlimit->arg); /* * We don't need ExclusiveLock since we aren't updating the flat file. */ rel = heap_open(DatabaseRelationId, RowExclusiveLock); ScanKeyInit(&scankey, Anum_pg_database_datname, BTEqualStrategyNumber, F_NAMEEQ, NameGetDatum(stmt->dbname)); scan = systable_beginscan(rel, DatabaseNameIndexId, true, SnapshotNow, 1, &scankey); tuple = systable_getnext(scan); if (!HeapTupleIsValid(tuple)) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_DATABASE), errmsg("database \"%s\" does not exist", stmt->dbname))); if (!pg_database_ownercheck(HeapTupleGetOid(tuple), GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_DATABASE, stmt->dbname); /* * Build an updated tuple, perusing the information just obtained */ MemSet(new_record, 0, sizeof(new_record)); MemSet(new_record_nulls, ' ', sizeof(new_record_nulls)); MemSet(new_record_repl, ' ', sizeof(new_record_repl)); if (dconnlimit) { new_record[Anum_pg_database_datconnlimit - 1] = Int32GetDatum(connlimit); new_record_repl[Anum_pg_database_datconnlimit - 1] = 'r'; } newtuple = heap_modifytuple(tuple, RelationGetDescr(rel), new_record, new_record_nulls, new_record_repl); simple_heap_update(rel, &tuple->t_self, newtuple); /* Update indexes */ CatalogUpdateIndexes(rel, newtuple); systable_endscan(scan); /* Close pg_database, but keep lock till commit */ heap_close(rel, NoLock); /* * We don't bother updating the flat file since the existing options for * ALTER DATABASE don't affect it. */}/* * ALTER DATABASE name SET ... */voidAlterDatabaseSet(AlterDatabaseSetStmt *stmt){ char *valuestr; HeapTuple tuple, newtuple; Relation rel; ScanKeyData scankey; SysScanDesc scan; Datum repl_val[Natts_pg_database]; char repl_null[Natts_pg_database]; char repl_repl[Natts_pg_database]; valuestr = flatten_set_variable_args(stmt->variable, stmt->value); /* * We don't need ExclusiveLock since we aren't updating the flat file. */ rel = heap_open(DatabaseRelationId, RowExclusiveLock); ScanKeyInit(&scankey, Anum_pg_database_datname, BTEqualStrategyNumber, F_NAMEEQ, NameGetDatum(stmt->dbname)); scan = systable_beginscan(rel, DatabaseNameIndexId, true, SnapshotNow, 1, &scankey); tuple = systable_getnext(scan); if (!HeapTupleIsValid(tuple)) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_DATABASE), errmsg("database \"%s\" does not exist", stmt->dbname))); if (!pg_database_ownercheck(HeapTupleGetOid(tuple), GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_DATABASE, stmt->dbname); MemSet(repl_repl, ' ', sizeof(repl_repl)); repl_repl[Anum_pg_database_datconfig - 1] = 'r'; if (strcmp(stmt->variable, "all") == 0 && valuestr == NULL) { /* RESET ALL */ repl_null[Anum_pg_database_datconfig - 1] = 'n'; repl_val[Anum_pg_database_datconfig - 1] = (Datum) 0; } else { Datum datum; bool isnull; ArrayType *a; repl_null[Anum_pg_database_datconfig - 1] = ' '; datum = heap_getattr(tuple, Anum_pg_database_datconfig, RelationGetDescr(rel), &isnull); a = isnull ? NULL : DatumGetArrayTypeP(datum);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -