]> granicus.if.org Git - postgresql/blobdiff - src/backend/commands/dbcommands.c
This patch implement the TODO [ALTER DATABASE foo OWNER TO bar].
[postgresql] / src / backend / commands / dbcommands.c
index 70678b26b08b0cc185349af3784df54ca6cab2cf..02c1bf8e2042a9441486593ba0229765eab049fc 100644 (file)
@@ -4,12 +4,12 @@
  *             Database management commands (create/drop database).
  *
  *
- * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/commands/dbcommands.c,v 1.119 2003/08/01 00:15:19 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/commands/dbcommands.c,v 1.134 2004/05/26 13:56:45 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -30,6 +30,7 @@
 #include "commands/comment.h"
 #include "commands/dbcommands.h"
 #include "miscadmin.h"
+#include "storage/fd.h"
 #include "storage/freespace.h"
 #include "storage/sinval.h"
 #include "utils/acl.h"
@@ -63,7 +64,9 @@ createdb(const CreatedbStmt *stmt)
        char       *alt_loc;
        char       *target_dir;
        char            src_loc[MAXPGPATH];
+#ifndef WIN32
        char            buf[2 * MAXPGPATH + 100];
+#endif
        Oid                     src_dboid;
        AclId           src_owner;
        int                     src_encoding;
@@ -79,7 +82,7 @@ createdb(const CreatedbStmt *stmt)
        char            new_record_nulls[Natts_pg_database];
        Oid                     dboid;
        AclId           datdba;
-       List       *option;
+       ListCell   *option;
        DefElem    *downer = NULL;
        DefElem    *dpath = NULL;
        DefElem    *dtemplate = NULL;
@@ -200,7 +203,7 @@ createdb(const CreatedbStmt *stmt)
        if (dbpath != NULL)
                ereport(ERROR,
                                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-                                errmsg("cannot use an alternate location on this platform")));
+                  errmsg("cannot use an alternative location on this platform")));
 #endif
 
        /*
@@ -228,7 +231,7 @@ createdb(const CreatedbStmt *stmt)
                                         src_dbpath))
                ereport(ERROR,
                                (errcode(ERRCODE_UNDEFINED_DATABASE),
-                                errmsg("template \"%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
@@ -260,8 +263,8 @@ createdb(const CreatedbStmt *stmt)
        if (DatabaseHasActiveBackends(src_dboid, true))
                ereport(ERROR,
                                (errcode(ERRCODE_OBJECT_IN_USE),
-                                errmsg("source database \"%s\" is being accessed by other users",
-                                               dbtemplate)));
+               errmsg("source database \"%s\" is being accessed by other users",
+                          dbtemplate)));
 
        /* If encoding is defaulted, use source's encoding */
        if (encoding < 0)
@@ -271,7 +274,7 @@ createdb(const CreatedbStmt *stmt)
        if (!PG_VALID_BE_ENCODING(encoding))
                ereport(ERROR,
                                (errcode(ERRCODE_WRONG_OBJECT_TYPE),
-                                errmsg("invalid backend encoding %d", encoding)));
+                                errmsg("invalid server encoding %d", encoding)));
 
        /*
         * Preassign OID for pg_database tuple, so that we can compute db
@@ -317,7 +320,7 @@ createdb(const CreatedbStmt *stmt)
         * up-to-date for the copy.  (We really only need to flush buffers for
         * the source database...)
         */
-       BufferSync();
+       BufferSync(-1, -1);
 
        /*
         * Close virtual file descriptors so the kernel has more available for
@@ -339,34 +342,56 @@ createdb(const CreatedbStmt *stmt)
        if (rmdir(target_dir) != 0)
                ereport(ERROR,
                                (errcode_for_file_access(),
-                                errmsg("could not remove temp directory \"%s\": %m",
+                                errmsg("could not remove temporary directory \"%s\": %m",
                                                target_dir)));
 
        /* Make the symlink, if needed */
        if (alt_loc)
        {
-#ifdef HAVE_SYMLINK    /* already throws error above */
+#ifdef HAVE_SYMLINK                            /* already throws error above */
                if (symlink(alt_loc, nominal_loc) != 0)
 #endif
                        ereport(ERROR,
                                        (errcode_for_file_access(),
-                                        errmsg("could not link \"%s\" to \"%s\": %m",
+                                        errmsg("could not link file \"%s\" to \"%s\": %m",
                                                        nominal_loc, alt_loc)));
        }
 
-       /* Copy the template database to the new location */
+       /*
+        * Copy the template database to the new location
+        *
+        * XXX use of cp really makes this code pretty grotty, particularly
+        * with respect to lack of ability to report errors well.  Someday
+        * rewrite to do it for ourselves.
+        */
 #ifndef WIN32
+       /* We might need to use cp -R one day for portability */
        snprintf(buf, sizeof(buf), "cp -r '%s' '%s'", src_loc, target_dir);
        if (system(buf) != 0)
-#else
+       {
+               if (remove_dbdirs(nominal_loc, alt_loc))
+                       ereport(ERROR,
+                                       (errmsg("could not initialize database directory"),
+                                        errdetail("Failing system command was: %s", buf),
+                                        errhint("Look in the postmaster's stderr log for more information.")));
+               else
+                       ereport(ERROR,
+                                       (errmsg("could not initialize database directory; delete failed as well"),
+                                        errdetail("Failing system command was: %s", buf),
+                                        errhint("Look in the postmaster's stderr log for more information.")));
+       }
+#else  /* WIN32 */
        if (copydir(src_loc, target_dir) != 0)
-#endif
        {
+               /* copydir should already have given details of its troubles */
                if (remove_dbdirs(nominal_loc, alt_loc))
-                       elog(ERROR, "could not initialize database directory");
+                       ereport(ERROR,
+                                       (errmsg("could not initialize database directory")));
                else
-                       elog(ERROR, "could not initialize database directory; delete failed as well");
+                       ereport(ERROR,
+                                       (errmsg("could not initialize database directory; delete failed as well")));
        }
+#endif /* WIN32 */
 
        /*
         * Now OK to grab exclusive lock on pg_database.
@@ -433,7 +458,7 @@ createdb(const CreatedbStmt *stmt)
         * will see the new database in pg_database right away.  (They'll see
         * an uncommitted tuple, but they don't care; see GetRawDatabaseInfo.)
         */
-       BufferSync();
+       BufferSync(-1, -1);
 }
 
 
@@ -450,7 +475,7 @@ dropdb(const char *dbname)
        char       *nominal_loc;
        char            dbpath[MAXPGPATH];
        Relation        pgdbrel;
-       SysScanDesc     pgdbscan;
+       SysScanDesc pgdbscan;
        ScanKeyData key;
        HeapTuple       tup;
 
@@ -503,16 +528,19 @@ dropdb(const char *dbname)
        if (DatabaseHasActiveBackends(db_id, false))
                ereport(ERROR,
                                (errcode(ERRCODE_OBJECT_IN_USE),
-                                errmsg("database \"%s\" is being accessed by other users",
-                                               dbname)));
+                          errmsg("database \"%s\" is being accessed by other users",
+                                         dbname)));
 
        /*
         * Find the database's tuple by OID (should be unique).
         */
-       ScanKeyEntryInitialize(&key, 0, ObjectIdAttributeNumber,
-                                                  F_OIDEQ, ObjectIdGetDatum(db_id));
+       ScanKeyInit(&key,
+                               ObjectIdAttributeNumber,
+                               BTEqualStrategyNumber, F_OIDEQ,
+                               ObjectIdGetDatum(db_id));
 
-       pgdbscan = systable_beginscan(pgdbrel, DatabaseOidIndex, true, SnapshotNow, 1, &key);
+       pgdbscan = systable_beginscan(pgdbrel, DatabaseOidIndex, true,
+                                                                 SnapshotNow, 1, &key);
 
        tup = systable_getnext(pgdbscan);
        if (!HeapTupleIsValid(tup))
@@ -567,7 +595,7 @@ dropdb(const char *dbname)
         * (They'll see an uncommitted deletion, but they don't care; see
         * GetRawDatabaseInfo.)
         */
-       BufferSync();
+       BufferSync(-1, -1);
 }
 
 
@@ -577,10 +605,13 @@ dropdb(const char *dbname)
 void
 RenameDatabase(const char *oldname, const char *newname)
 {
-       HeapTuple       tup, newtup;
+       HeapTuple       tup,
+                               newtup;
        Relation        rel;
-       SysScanDesc     scan, scan2;
-       ScanKeyData     key, key2;
+       SysScanDesc scan,
+                               scan2;
+       ScanKeyData key,
+                               key2;
 
        /*
         * Obtain AccessExclusiveLock so that no new session gets started
@@ -588,9 +619,12 @@ RenameDatabase(const char *oldname, const char *newname)
         */
        rel = heap_openr(DatabaseRelationName, AccessExclusiveLock);
 
-       ScanKeyEntryInitialize(&key, 0, Anum_pg_database_datname,
-                                                  F_NAMEEQ, NameGetDatum(oldname));
-       scan = systable_beginscan(rel, DatabaseNameIndex, true, SnapshotNow, 1, &key);
+       ScanKeyInit(&key,
+                               Anum_pg_database_datname,
+                               BTEqualStrategyNumber, F_NAMEEQ,
+                               NameGetDatum(oldname));
+       scan = systable_beginscan(rel, DatabaseNameIndex, true,
+                                                         SnapshotNow, 1, &key);
 
        tup = systable_getnext(scan);
        if (!HeapTupleIsValid(tup))
@@ -610,20 +644,22 @@ RenameDatabase(const char *oldname, const char *newname)
                                 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.  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)));
+                          errmsg("database \"%s\" is being accessed by other users",
+                                         oldname)));
 
        /* make sure the new name doesn't exist */
-       ScanKeyEntryInitialize(&key2, 0, Anum_pg_database_datname,
-                                                  F_NAMEEQ, NameGetDatum(newname));
-       scan2 = systable_beginscan(rel, DatabaseNameIndex, true, SnapshotNow, 1, &key2);
+       ScanKeyInit(&key2,
+                               Anum_pg_database_datname,
+                               BTEqualStrategyNumber, F_NAMEEQ,
+                               NameGetDatum(newname));
+       scan2 = systable_beginscan(rel, DatabaseNameIndex, true,
+                                                          SnapshotNow, 1, &key2);
        if (HeapTupleIsValid(systable_getnext(scan2)))
                ereport(ERROR,
                                (errcode(ERRCODE_DUPLICATE_DATABASE),
@@ -651,12 +687,12 @@ RenameDatabase(const char *oldname, const char *newname)
        heap_close(rel, NoLock);
 
        /*
-        * Force dirty buffers out to disk, so that newly-connecting
-        * backends will see the renamed database in pg_database right
-        * away.  (They'll see an uncommitted tuple, but they don't care;
-        * see GetRawDatabaseInfo.)
+        * Force dirty buffers out to disk, so that newly-connecting backends
+        * will see the renamed database in pg_database right away.  (They'll
+        * see an uncommitted tuple, but they don't care; see
+        * GetRawDatabaseInfo.)
         */
-       BufferSync();
+       BufferSync(-1, -1);
 }
 
 
@@ -671,7 +707,7 @@ AlterDatabaseSet(AlterDatabaseSetStmt *stmt)
                                newtuple;
        Relation        rel;
        ScanKeyData scankey;
-       SysScanDesc     scan;
+       SysScanDesc scan;
        Datum           repl_val[Natts_pg_database];
        char            repl_null[Natts_pg_database];
        char            repl_repl[Natts_pg_database];
@@ -679,9 +715,12 @@ AlterDatabaseSet(AlterDatabaseSetStmt *stmt)
        valuestr = flatten_set_variable_args(stmt->variable, stmt->value);
 
        rel = heap_openr(DatabaseRelationName, RowExclusiveLock);
-       ScanKeyEntryInitialize(&scankey, 0, Anum_pg_database_datname,
-                                                  F_NAMEEQ, NameGetDatum(stmt->dbname));
-       scan = systable_beginscan(rel, DatabaseNameIndex, true, SnapshotNow, 1, &scankey);
+       ScanKeyInit(&scankey,
+                               Anum_pg_database_datname,
+                               BTEqualStrategyNumber, F_NAMEEQ,
+                               NameGetDatum(stmt->dbname));
+       scan = systable_beginscan(rel, DatabaseNameIndex, true,
+                                                         SnapshotNow, 1, &scankey);
        tuple = systable_getnext(scan);
        if (!HeapTupleIsValid(tuple))
                ereport(ERROR,
@@ -689,9 +728,9 @@ AlterDatabaseSet(AlterDatabaseSetStmt *stmt)
                                 errmsg("database \"%s\" does not exist", stmt->dbname)));
 
        if (!(superuser()
-                 || ((Form_pg_database) GETSTRUCT(tuple))->datdba == GetUserId()))
+               || ((Form_pg_database) GETSTRUCT(tuple))->datdba == GetUserId()))
                aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_DATABASE,
-                                         stmt->dbname);
+                                          stmt->dbname);
 
        MemSet(repl_repl, ' ', sizeof(repl_repl));
        repl_repl[Anum_pg_database_datconfig - 1] = 'r';
@@ -713,7 +752,7 @@ AlterDatabaseSet(AlterDatabaseSetStmt *stmt)
                datum = heap_getattr(tuple, Anum_pg_database_datconfig,
                                                         RelationGetDescr(rel), &isnull);
 
-               a = isnull ? ((ArrayType *) NULL) : DatumGetArrayTypeP(datum);
+               a = isnull ? NULL : DatumGetArrayTypeP(datum);
 
                if (valuestr)
                        a = GUCArrayAdd(a, stmt->variable, valuestr);
@@ -737,6 +776,52 @@ AlterDatabaseSet(AlterDatabaseSetStmt *stmt)
 }
 
 
+/*
+ * ALTER DATABASE name OWNER TO newowner
+ */
+void
+AlterDatabaseOwner(const char *dbname, const char *newowner)
+{
+       AclId           newdatdba;
+       HeapTuple       tuple,
+                               newtuple;
+       Relation        rel;
+       ScanKeyData scankey;
+       SysScanDesc scan;
+
+       rel = heap_openr(DatabaseRelationName, RowExclusiveLock);
+       ScanKeyInit(&scankey,
+                               Anum_pg_database_datname,
+                               BTEqualStrategyNumber, F_NAMEEQ,
+                               NameGetDatum(dbname));
+       scan = systable_beginscan(rel, DatabaseNameIndex, true,
+                                                         SnapshotNow, 1, &scankey);
+       tuple = systable_getnext(scan);
+       if (!HeapTupleIsValid(tuple))
+               ereport(ERROR,
+                               (errcode(ERRCODE_UNDEFINED_DATABASE),
+                                errmsg("database \"%s\" does not exist", dbname)));
+
+       /* obtain sysid of proposed owner */
+       newdatdba = get_usesysid(newowner); /* will ereport if no such user */
+
+       /* changing owner's database for someone else: must be superuser */
+       /* note that the someone else need not have any permissions */
+       if (!superuser())
+               ereport(ERROR,
+                               (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+                                errmsg("must be superuser to change owner's database for another user")));
+
+       /* change owner */
+       newtuple = heap_copytuple(tuple);
+       ((Form_pg_database) GETSTRUCT(newtuple))->datdba = newdatdba;
+       simple_heap_update(rel, &tuple->t_self, newtuple);
+       CatalogUpdateIndexes(rel, newtuple);
+
+       systable_endscan(scan);
+       heap_close(rel, NoLock);
+}
+
 
 /*
  * Helper functions
@@ -750,7 +835,7 @@ get_db_info(const char *name, Oid *dbIdP, int4 *ownerIdP,
 {
        Relation        relation;
        ScanKeyData scanKey;
-       SysScanDesc     scan;
+       SysScanDesc scan;
        HeapTuple       tuple;
        bool            gottuple;
 
@@ -759,10 +844,13 @@ get_db_info(const char *name, Oid *dbIdP, int4 *ownerIdP,
        /* Caller may wish to grab a better lock on pg_database beforehand... */
        relation = heap_openr(DatabaseRelationName, AccessShareLock);
 
-       ScanKeyEntryInitialize(&scanKey, 0, Anum_pg_database_datname,
-                                                  F_NAMEEQ, NameGetDatum(name));
+       ScanKeyInit(&scanKey,
+                               Anum_pg_database_datname,
+                               BTEqualStrategyNumber, F_NAMEEQ,
+                               NameGetDatum(name));
 
-       scan = systable_beginscan(relation, DatabaseNameIndex, true, SnapshotNow, 1, &scanKey);
+       scan = systable_beginscan(relation, DatabaseNameIndex, true,
+                                                         SnapshotNow, 1, &scanKey);
 
        tuple = systable_getnext(scan);
 
@@ -862,7 +950,7 @@ resolve_alt_dbpath(const char *dbpath, Oid dboid)
 #ifndef ALLOW_ABSOLUTE_DBPATHS
                ereport(ERROR,
                                (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
-                                errmsg("absolute paths are not allowed as database locations")));
+               errmsg("absolute paths are not allowed as database locations")));
 #endif
                prefix = dbpath;
        }
@@ -874,8 +962,8 @@ resolve_alt_dbpath(const char *dbpath, Oid dboid)
                if (!var)
                        ereport(ERROR,
                                        (errcode(ERRCODE_UNDEFINED_OBJECT),
-                                        errmsg("postmaster environment variable \"%s\" not found",
-                                                       dbpath)));
+                          errmsg("postmaster environment variable \"%s\" not found",
+                                         dbpath)));
                if (!is_absolute_path(var))
                        ereport(ERROR,
                                        (errcode(ERRCODE_INVALID_NAME),
@@ -888,7 +976,7 @@ resolve_alt_dbpath(const char *dbpath, Oid dboid)
        if (len >= MAXPGPATH - 100)
                ereport(ERROR,
                                (errcode(ERRCODE_INVALID_NAME),
-                                errmsg("alternate path is too long")));
+                                errmsg("alternative path is too long")));
 
        ret = palloc(len);
        snprintf(ret, len, "%s/base/%u", prefix, dboid);
@@ -919,7 +1007,7 @@ remove_dbdirs(const char *nominal_loc, const char *alt_loc)
                {
                        ereport(WARNING,
                                        (errcode_for_file_access(),
-                                        errmsg("could not remove \"%s\": %m", nominal_loc)));
+                                        errmsg("could not remove file \"%s\": %m", nominal_loc)));
                        success = false;
                }
        }
@@ -933,9 +1021,10 @@ remove_dbdirs(const char *nominal_loc, const char *alt_loc)
        if (system(buf) != 0)
        {
                ereport(WARNING,
-                               (errcode_for_file_access(),
-                                errmsg("could not remove database directory \"%s\": %m",
-                                               target_dir)));
+                               (errmsg("could not remove database directory \"%s\"",
+                                               target_dir),
+                                errdetail("Failing system command was: %s", buf),
+                                errhint("Look in the postmaster's stderr log for more information.")));
                success = false;
        }
 
@@ -955,16 +1044,18 @@ get_database_oid(const char *dbname)
 {
        Relation        pg_database;
        ScanKeyData entry[1];
-       SysScanDesc     scan;
+       SysScanDesc scan;
        HeapTuple       dbtuple;
        Oid                     oid;
 
        /* There's no syscache for pg_database, so must look the hard way */
        pg_database = heap_openr(DatabaseRelationName, AccessShareLock);
-       ScanKeyEntryInitialize(&entry[0], 0x0,
-                                                  Anum_pg_database_datname, F_NAMEEQ,
-                                                  CStringGetDatum(dbname));
-       scan = systable_beginscan(pg_database, DatabaseNameIndex, true, SnapshotNow, 1, entry);
+       ScanKeyInit(&entry[0],
+                               Anum_pg_database_datname,
+                               BTEqualStrategyNumber, F_NAMEEQ,
+                               CStringGetDatum(dbname));
+       scan = systable_beginscan(pg_database, DatabaseNameIndex, true,
+                                                         SnapshotNow, 1, entry);
 
        dbtuple = systable_getnext(scan);
 
@@ -993,16 +1084,18 @@ get_database_name(Oid dbid)
 {
        Relation        pg_database;
        ScanKeyData entry[1];
-       SysScanDesc     scan;
+       SysScanDesc scan;
        HeapTuple       dbtuple;
        char       *result;
 
        /* There's no syscache for pg_database, so must look the hard way */
        pg_database = heap_openr(DatabaseRelationName, AccessShareLock);
-       ScanKeyEntryInitialize(&entry[0], 0x0,
-                                                  ObjectIdAttributeNumber, F_OIDEQ,
-                                                  ObjectIdGetDatum(dbid));
-       scan = systable_beginscan(pg_database, DatabaseOidIndex, true, SnapshotNow, 1, entry);
+       ScanKeyInit(&entry[0],
+                               ObjectIdAttributeNumber,
+                               BTEqualStrategyNumber, F_OIDEQ,
+                               ObjectIdGetDatum(dbid));
+       scan = systable_beginscan(pg_database, DatabaseOidIndex, true,
+                                                         SnapshotNow, 1, entry);
 
        dbtuple = systable_getnext(scan);