1996-07-09 08:22:35 +02:00
|
|
|
/*-------------------------------------------------------------------------
|
|
|
|
*
|
|
|
|
* dbcommands.c--
|
1997-09-07 07:04:48 +02:00
|
|
|
*
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
|
|
|
* Copyright (c) 1994, Regents of the University of California
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* IDENTIFICATION
|
1998-10-05 04:49:36 +02:00
|
|
|
* $Header: /cvsroot/pgsql/src/backend/commands/dbcommands.c,v 1.25 1998/10/05 02:49:36 thomas Exp $
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
#include <stdio.h>
|
1997-01-10 21:19:49 +01:00
|
|
|
#include <string.h>
|
1996-07-09 08:22:35 +02:00
|
|
|
#include <signal.h>
|
1997-11-07 07:38:51 +01:00
|
|
|
#include <sys/stat.h>
|
1996-07-09 08:22:35 +02:00
|
|
|
|
|
|
|
#include "postgres.h"
|
1998-04-27 06:08:07 +02:00
|
|
|
|
1996-07-09 08:22:35 +02:00
|
|
|
#include "access/heapam.h"
|
|
|
|
#include "access/htup.h"
|
|
|
|
#include "access/relscan.h"
|
|
|
|
#include "catalog/catname.h"
|
|
|
|
#include "catalog/pg_database.h"
|
1998-04-27 06:08:07 +02:00
|
|
|
#include "catalog/pg_shadow.h"
|
1997-11-24 06:32:56 +01:00
|
|
|
#include "commands/dbcommands.h"
|
1998-04-27 06:08:07 +02:00
|
|
|
#include "fmgr.h"
|
|
|
|
#include "miscadmin.h" /* for DataDir */
|
1996-07-09 08:22:35 +02:00
|
|
|
#include "storage/bufmgr.h"
|
1997-08-18 04:15:04 +02:00
|
|
|
#include "storage/fd.h"
|
1998-04-27 06:08:07 +02:00
|
|
|
#include "storage/lmgr.h"
|
|
|
|
#include "tcop/tcopprot.h"
|
|
|
|
#include "utils/rel.h"
|
|
|
|
#include "utils/syscache.h"
|
1996-07-09 08:22:35 +02:00
|
|
|
|
|
|
|
|
|
|
|
/* non-export function prototypes */
|
1998-09-01 06:40:42 +02:00
|
|
|
static void check_permissions(char *command, char *dbpath, char *dbname,
|
1998-08-11 20:28:49 +02:00
|
|
|
Oid *dbIdP, int4 *userIdP);
|
1996-07-09 08:22:35 +02:00
|
|
|
static HeapTuple get_pg_dbtup(char *command, char *dbname, Relation dbrel);
|
1997-11-07 07:38:51 +01:00
|
|
|
static void stop_vacuum(char *dbpath, char *dbname);
|
1996-07-09 08:22:35 +02:00
|
|
|
|
|
|
|
void
|
1998-08-24 03:14:24 +02:00
|
|
|
createdb(char *dbname, char *dbpath, int encoding)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1998-08-29 06:09:29 +02:00
|
|
|
Oid db_id;
|
1998-09-01 06:40:42 +02:00
|
|
|
int4 user_id;
|
1997-09-08 04:41:22 +02:00
|
|
|
char buf[512];
|
1997-11-07 07:38:51 +01:00
|
|
|
char *lp,
|
|
|
|
loc[512];
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* If this call returns, the database does not exist and we're allowed
|
|
|
|
* to create databases.
|
|
|
|
*/
|
1997-11-07 07:38:51 +01:00
|
|
|
check_permissions("createdb", dbpath, dbname, &db_id, &user_id);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
/* close virtual file descriptors so we can do system() calls */
|
|
|
|
closeAllVfds();
|
|
|
|
|
1997-11-07 07:38:51 +01:00
|
|
|
/* Now create directory for this new database */
|
1998-02-26 05:46:47 +01:00
|
|
|
if ((dbpath != NULL) && (strcmp(dbpath, dbname) != 0))
|
1997-11-07 07:38:51 +01:00
|
|
|
{
|
1998-02-26 05:46:47 +01:00
|
|
|
if (*(dbpath + strlen(dbpath) - 1) == SEP_CHAR)
|
|
|
|
*(dbpath + strlen(dbpath) - 1) = '\0';
|
1997-11-07 07:38:51 +01:00
|
|
|
sprintf(loc, "%s%c%s", dbpath, SEP_CHAR, dbname);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
strcpy(loc, dbname);
|
|
|
|
|
|
|
|
lp = ExpandDatabasePath(loc);
|
|
|
|
|
1997-11-10 16:17:44 +01:00
|
|
|
if (lp == NULL)
|
1998-02-26 05:46:47 +01:00
|
|
|
elog(ERROR, "Unable to locate path '%s'"
|
|
|
|
"\n\tThis may be due to a missing environment variable"
|
|
|
|
" in the server", loc);
|
1997-11-10 16:17:44 +01:00
|
|
|
|
1998-02-26 05:46:47 +01:00
|
|
|
if (mkdir(lp, S_IRWXU) != 0)
|
1998-10-05 04:49:36 +02:00
|
|
|
elog(ERROR, "Unable to create database directory '%s'", lp);
|
1997-11-07 07:38:51 +01:00
|
|
|
|
|
|
|
sprintf(buf, "%s %s%cbase%ctemplate1%c* %s",
|
|
|
|
COPY_CMD, DataDir, SEP_CHAR, SEP_CHAR, SEP_CHAR, lp);
|
1997-09-07 07:04:48 +02:00
|
|
|
system(buf);
|
|
|
|
|
1997-11-07 07:38:51 +01:00
|
|
|
#if FALSE
|
1997-09-07 07:04:48 +02:00
|
|
|
sprintf(buf, "insert into pg_database (datname, datdba, datpath) \
|
1998-10-05 04:49:36 +02:00
|
|
|
values ('%s'::name, '%d'::oid, '%s'::text);",
|
1997-09-07 07:04:48 +02:00
|
|
|
dbname, user_id, dbname);
|
1997-11-07 07:38:51 +01:00
|
|
|
#endif
|
|
|
|
|
1998-08-24 03:14:24 +02:00
|
|
|
sprintf(buf, "insert into pg_database (datname, datdba, encoding, datpath)"
|
1998-10-05 04:49:36 +02:00
|
|
|
" values ('%s', '%d', '%d', '%s');", dbname, user_id, encoding, loc);
|
1998-08-24 03:14:24 +02:00
|
|
|
|
1996-07-09 08:22:35 +02:00
|
|
|
|
1998-06-04 19:26:49 +02:00
|
|
|
pg_exec_query(buf);
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
destroydb(char *dbname)
|
|
|
|
{
|
1998-09-01 06:40:42 +02:00
|
|
|
int4 user_id;
|
1998-08-29 06:09:29 +02:00
|
|
|
Oid db_id;
|
1997-11-07 07:38:51 +01:00
|
|
|
char *path;
|
1998-02-26 05:46:47 +01:00
|
|
|
char dbpath[MAXPGPATH + 1];
|
1997-09-08 04:41:22 +02:00
|
|
|
char buf[512];
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* If this call returns, the database exists and we're allowed to
|
|
|
|
* remove it.
|
|
|
|
*/
|
1997-11-07 07:38:51 +01:00
|
|
|
check_permissions("destroydb", dbpath, dbname, &db_id, &user_id);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
if (!OidIsValid(db_id))
|
1998-10-05 04:49:36 +02:00
|
|
|
elog(FATAL, "pg_database instance has an invalid OID");
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
/* stop the vacuum daemon */
|
1997-11-07 07:38:51 +01:00
|
|
|
stop_vacuum(dbpath, dbname);
|
|
|
|
|
1997-11-10 16:17:44 +01:00
|
|
|
path = ExpandDatabasePath(dbpath);
|
1998-02-26 05:46:47 +01:00
|
|
|
if (path == NULL)
|
|
|
|
elog(ERROR, "Unable to locate path '%s'"
|
|
|
|
"\n\tThis may be due to a missing environment variable"
|
|
|
|
" in the server", dbpath);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* remove the pg_database tuple FIRST, this may fail due to
|
|
|
|
* permissions problems
|
|
|
|
*/
|
|
|
|
sprintf(buf, "delete from pg_database where pg_database.oid = \'%d\'::oid",
|
|
|
|
db_id);
|
1998-06-04 19:26:49 +02:00
|
|
|
pg_exec_query(buf);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* remove the data directory. If the DELETE above failed, this will
|
|
|
|
* not be reached
|
|
|
|
*/
|
1997-11-07 07:38:51 +01:00
|
|
|
|
|
|
|
sprintf(buf, "rm -r %s", path);
|
1997-09-07 07:04:48 +02:00
|
|
|
system(buf);
|
|
|
|
|
|
|
|
/* drop pages for this database that are in the shared buffer cache */
|
|
|
|
DropBuffers(db_id);
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
1997-09-08 04:41:22 +02:00
|
|
|
static HeapTuple
|
1996-07-09 08:22:35 +02:00
|
|
|
get_pg_dbtup(char *command, char *dbname, Relation dbrel)
|
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
HeapTuple dbtup;
|
|
|
|
HeapTuple tup;
|
|
|
|
HeapScanDesc scan;
|
|
|
|
ScanKeyData scanKey;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
ScanKeyEntryInitialize(&scanKey, 0, Anum_pg_database_datname,
|
1998-04-27 06:08:07 +02:00
|
|
|
F_NAMEEQ, NameGetDatum(dbname));
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1998-07-27 21:38:40 +02:00
|
|
|
scan = heap_beginscan(dbrel, 0, SnapshotNow, 1, &scanKey);
|
1997-09-07 07:04:48 +02:00
|
|
|
if (!HeapScanIsValid(scan))
|
1998-10-05 04:49:36 +02:00
|
|
|
elog(ERROR, "%s: cannot begin scan of pg_database", command);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* since we want to return the tuple out of this proc, and we're going
|
|
|
|
* to close the relation, copy the tuple and return the copy.
|
|
|
|
*/
|
1998-08-19 04:04:17 +02:00
|
|
|
tup = heap_getnext(scan, 0);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
if (HeapTupleIsValid(tup))
|
|
|
|
dbtup = heap_copytuple(tup);
|
|
|
|
else
|
|
|
|
dbtup = tup;
|
|
|
|
|
|
|
|
heap_endscan(scan);
|
1998-09-01 05:29:17 +02:00
|
|
|
return dbtup;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
1997-09-07 07:04:48 +02:00
|
|
|
* check_permissions() -- verify that the user is permitted to do this.
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
1997-09-07 07:04:48 +02:00
|
|
|
* If the user is not allowed to carry out this operation, this routine
|
1998-01-05 17:40:20 +01:00
|
|
|
* elog(ERROR, ...)s, which will abort the xact. As a side effect, the
|
1997-09-07 07:04:48 +02:00
|
|
|
* user's pg_user tuple OID is returned in userIdP and the target database's
|
|
|
|
* OID is returned in dbIdP.
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
static void
|
|
|
|
check_permissions(char *command,
|
1997-11-07 07:38:51 +01:00
|
|
|
char *dbpath,
|
1997-09-07 07:04:48 +02:00
|
|
|
char *dbname,
|
1997-09-08 23:56:23 +02:00
|
|
|
Oid *dbIdP,
|
1998-08-11 20:28:49 +02:00
|
|
|
int4 *userIdP)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
Relation dbrel;
|
|
|
|
HeapTuple dbtup,
|
|
|
|
utup;
|
1998-08-11 20:28:49 +02:00
|
|
|
int4 dbowner = 0;
|
1997-09-08 04:41:22 +02:00
|
|
|
char use_createdb;
|
|
|
|
bool dbfound;
|
|
|
|
bool use_super;
|
|
|
|
char *userName;
|
1997-11-07 07:38:51 +01:00
|
|
|
text *dbtext;
|
1998-02-26 05:46:47 +01:00
|
|
|
char path[MAXPGPATH + 1];
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
userName = GetPgUserName();
|
1998-08-19 04:04:17 +02:00
|
|
|
utup = SearchSysCacheTuple(USENAME,
|
1998-09-01 06:40:42 +02:00
|
|
|
PointerGetDatum(userName),
|
|
|
|
0, 0, 0);
|
1998-02-25 14:09:49 +01:00
|
|
|
*userIdP = ((Form_pg_shadow) GETSTRUCT(utup))->usesysid;
|
|
|
|
use_super = ((Form_pg_shadow) GETSTRUCT(utup))->usesuper;
|
|
|
|
use_createdb = ((Form_pg_shadow) GETSTRUCT(utup))->usecreatedb;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
/* Check to make sure user has permission to use createdb */
|
|
|
|
if (!use_createdb)
|
|
|
|
{
|
1998-10-05 04:49:36 +02:00
|
|
|
elog(ERROR, "user '%s' is not allowed to create/destroy databases",
|
1997-09-07 07:04:48 +02:00
|
|
|
userName);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Make sure we are not mucking with the template database */
|
|
|
|
if (!strcmp(dbname, "template1"))
|
1998-10-05 04:49:36 +02:00
|
|
|
elog(ERROR, "%s: cannot be executed on the template database", command);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
/* Check to make sure database is not the currently open database */
|
1998-04-05 23:04:50 +02:00
|
|
|
if (!strcmp(dbname, DatabaseName))
|
1998-10-05 04:49:36 +02:00
|
|
|
elog(ERROR, "%s: cannot be executed on an open database", command);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
/* Check to make sure database is owned by this user */
|
|
|
|
|
|
|
|
/*
|
|
|
|
* need the reldesc to get the database owner out of dbtup and to set
|
|
|
|
* a write lock on it.
|
|
|
|
*/
|
|
|
|
dbrel = heap_openr(DatabaseRelationName);
|
|
|
|
|
|
|
|
if (!RelationIsValid(dbrel))
|
|
|
|
elog(FATAL, "%s: cannot open relation \"%-.*s\"",
|
|
|
|
command, DatabaseRelationName);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Acquire a write lock on pg_database from the beginning to avoid
|
|
|
|
* upgrading a read lock to a write lock. Upgrading causes long
|
|
|
|
* delays when multiple 'createdb's or 'destroydb's are run simult.
|
|
|
|
* -mer 7/3/91
|
|
|
|
*/
|
|
|
|
RelationSetLockForWrite(dbrel);
|
|
|
|
dbtup = get_pg_dbtup(command, dbname, dbrel);
|
|
|
|
dbfound = HeapTupleIsValid(dbtup);
|
|
|
|
|
|
|
|
if (dbfound)
|
|
|
|
{
|
1998-08-11 20:28:49 +02:00
|
|
|
dbowner = (int4) heap_getattr(dbtup,
|
1998-09-01 06:40:42 +02:00
|
|
|
Anum_pg_database_datdba,
|
|
|
|
RelationGetDescr(dbrel),
|
|
|
|
(char *) NULL);
|
1997-09-07 07:04:48 +02:00
|
|
|
*dbIdP = dbtup->t_oid;
|
1998-01-31 05:39:26 +01:00
|
|
|
dbtext = (text *) heap_getattr(dbtup,
|
1998-02-26 05:46:47 +01:00
|
|
|
Anum_pg_database_datpath,
|
1998-09-01 05:29:17 +02:00
|
|
|
RelationGetDescr(dbrel),
|
1998-02-26 05:46:47 +01:00
|
|
|
(char *) NULL);
|
1997-11-07 07:38:51 +01:00
|
|
|
|
1998-02-26 05:46:47 +01:00
|
|
|
strncpy(path, VARDATA(dbtext), (VARSIZE(dbtext) - VARHDRSZ));
|
|
|
|
*(path + VARSIZE(dbtext) - VARHDRSZ) = '\0';
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
*dbIdP = InvalidOid;
|
|
|
|
|
|
|
|
heap_close(dbrel);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Now be sure that the user is allowed to do this.
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (dbfound && !strcmp(command, "createdb"))
|
|
|
|
{
|
|
|
|
|
1998-10-05 04:49:36 +02:00
|
|
|
elog(ERROR, "createdb: database '%s' already exists", dbname);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
}
|
|
|
|
else if (!dbfound && !strcmp(command, "destroydb"))
|
|
|
|
{
|
|
|
|
|
1998-10-05 04:49:36 +02:00
|
|
|
elog(ERROR, "destroydb: database '%s' does not exist", dbname);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
}
|
|
|
|
else if (dbfound && !strcmp(command, "destroydb")
|
|
|
|
&& dbowner != *userIdP && use_super == false)
|
|
|
|
{
|
|
|
|
|
1998-10-05 04:49:36 +02:00
|
|
|
elog(ERROR, "%s: database '%s' is not owned by you", command, dbname);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
}
|
1997-11-07 07:38:51 +01:00
|
|
|
|
|
|
|
if (dbfound && !strcmp(command, "destroydb"))
|
|
|
|
strcpy(dbpath, path);
|
1998-02-26 05:46:47 +01:00
|
|
|
} /* check_permissions() */
|
1996-07-09 08:22:35 +02:00
|
|
|
|
|
|
|
/*
|
1997-11-07 07:38:51 +01:00
|
|
|
* stop_vacuum() -- stop the vacuum daemon on the database, if one is running.
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
1997-08-19 23:40:56 +02:00
|
|
|
static void
|
1997-11-07 07:38:51 +01:00
|
|
|
stop_vacuum(char *dbpath, char *dbname)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
char filename[256];
|
|
|
|
FILE *fp;
|
|
|
|
int pid;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1997-11-07 07:38:51 +01:00
|
|
|
if (strchr(dbpath, SEP_CHAR) != 0)
|
|
|
|
{
|
|
|
|
sprintf(filename, "%s%cbase%c%s%c%s.vacuum", DataDir, SEP_CHAR, SEP_CHAR,
|
1998-02-26 05:46:47 +01:00
|
|
|
dbname, SEP_CHAR, dbname);
|
1997-11-07 07:38:51 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
sprintf(filename, "%s%c%s.vacuum", dbpath, SEP_CHAR, dbname);
|
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
if ((fp = AllocateFile(filename, "r")) != NULL)
|
|
|
|
{
|
|
|
|
fscanf(fp, "%d", &pid);
|
|
|
|
FreeFile(fp);
|
|
|
|
if (kill(pid, SIGKILLDAEMON1) < 0)
|
|
|
|
{
|
1998-10-05 04:49:36 +02:00
|
|
|
elog(ERROR, "can't kill vacuum daemon (pid %d) on '%s'",
|
1997-09-07 07:04:48 +02:00
|
|
|
pid, dbname);
|
|
|
|
}
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
}
|