dbcommands.c 8.22 KB
Newer Older
1 2 3
/*-------------------------------------------------------------------------
 *
 * dbcommands.c--
4
 *
5 6 7 8 9
 *
 * Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
10
 *	  $Header: /cvsroot/pgsql/src/backend/commands/dbcommands.c,v 1.11 1998/04/07 18:10:41 momjian Exp $
11 12 13 14
 *
 *-------------------------------------------------------------------------
 */
#include <stdio.h>
15
#include <string.h>
16
#include <signal.h>
17
#include <sys/stat.h>
18 19

#include "postgres.h"
20
#include "miscadmin.h"			/* for DataDir */
21 22 23 24 25 26 27
#include "access/heapam.h"
#include "access/htup.h"
#include "access/relscan.h"
#include "utils/rel.h"
#include "utils/elog.h"
#include "catalog/catname.h"
#include "catalog/pg_proc.h"
28
#include "catalog/pg_shadow.h"
29 30
#include "catalog/pg_database.h"
#include "utils/syscache.h"
31
#include "commands/dbcommands.h"
32 33 34
#include "tcop/tcopprot.h"
#include "storage/bufmgr.h"
#include "storage/lmgr.h"
35
#include "storage/fd.h"
36 37 38


/* non-export function prototypes */
39
static void
40
check_permissions(char *command, char *dbpath, char *dbname,
41
				  Oid *dbIdP, Oid *userIdP);
42
static HeapTuple get_pg_dbtup(char *command, char *dbname, Relation dbrel);
43
static void stop_vacuum(char *dbpath, char *dbname);
44 45

void
46
createdb(char *dbname, char *dbpath)
47
{
48 49 50
	Oid			db_id,
				user_id;
	char		buf[512];
51 52
	char	   *lp,
				loc[512];
53 54 55 56 57

	/*
	 * If this call returns, the database does not exist and we're allowed
	 * to create databases.
	 */
58
	check_permissions("createdb", dbpath, dbname, &db_id, &user_id);
59 60 61 62

	/* close virtual file descriptors so we can do system() calls */
	closeAllVfds();

63
	/* Now create directory for this new database */
64
	if ((dbpath != NULL) && (strcmp(dbpath, dbname) != 0))
65
	{
66 67
		if (*(dbpath + strlen(dbpath) - 1) == SEP_CHAR)
			*(dbpath + strlen(dbpath) - 1) = '\0';
68 69 70 71 72 73 74 75 76
		sprintf(loc, "%s%c%s", dbpath, SEP_CHAR, dbname);
	}
	else
	{
		strcpy(loc, dbname);
	}

	lp = ExpandDatabasePath(loc);

77
	if (lp == NULL)
78 79 80
		elog(ERROR, "Unable to locate path '%s'"
			 "\n\tThis may be due to a missing environment variable"
			 " in the server", loc);
81

82 83
	if (mkdir(lp, S_IRWXU) != 0)
		elog(ERROR, "Unable to create database directory %s", lp);
84 85 86

	sprintf(buf, "%s %s%cbase%ctemplate1%c* %s",
			COPY_CMD, DataDir, SEP_CHAR, SEP_CHAR, SEP_CHAR, lp);
87 88
	system(buf);

89
#if FALSE
90
	sprintf(buf, "insert into pg_database (datname, datdba, datpath) \
91
                  values (\'%s\'::char16, \'%d\'::oid, \'%s\'::text);",
92
			dbname, user_id, dbname);
93 94 95
#endif

	sprintf(buf, "insert into pg_database (datname, datdba, datpath)"
96
			" values (\'%s\', \'%d\', \'%s\');", dbname, user_id, loc);
97

98
	pg_exec_query(buf, (char **) NULL, (Oid *) NULL, 0);
99 100 101 102 103
}

void
destroydb(char *dbname)
{
104 105
	Oid			user_id,
				db_id;
106
	char	   *path;
107
	char		dbpath[MAXPGPATH + 1];
108
	char		buf[512];
109 110 111 112 113

	/*
	 * If this call returns, the database exists and we're allowed to
	 * remove it.
	 */
114
	check_permissions("destroydb", dbpath, dbname, &db_id, &user_id);
115 116 117 118 119 120 121

	if (!OidIsValid(db_id))
	{
		elog(FATAL, "impossible: pg_database instance with invalid OID.");
	}

	/* stop the vacuum daemon */
122 123
	stop_vacuum(dbpath, dbname);

124
	path = ExpandDatabasePath(dbpath);
125 126 127 128
	if (path == NULL)
		elog(ERROR, "Unable to locate path '%s'"
			 "\n\tThis may be due to a missing environment variable"
			 " in the server", dbpath);
129 130 131 132 133 134 135

	/*
	 * 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);
136
	pg_exec_query(buf, (char **) NULL, (Oid *) NULL, 0);
137 138 139 140 141

	/*
	 * remove the data directory. If the DELETE above failed, this will
	 * not be reached
	 */
142 143

	sprintf(buf, "rm -r %s", path);
144 145 146 147
	system(buf);

	/* drop pages for this database that are in the shared buffer cache */
	DropBuffers(db_id);
148 149
}

150
static HeapTuple
151 152
get_pg_dbtup(char *command, char *dbname, Relation dbrel)
{
153 154 155 156 157
	HeapTuple	dbtup;
	HeapTuple	tup;
	Buffer		buf;
	HeapScanDesc scan;
	ScanKeyData scanKey;
158 159 160 161

	ScanKeyEntryInitialize(&scanKey, 0, Anum_pg_database_datname,
						   NameEqualRegProcedure, NameGetDatum(dbname));

162
	scan = heap_beginscan(dbrel, 0, false, 1, &scanKey);
163
	if (!HeapScanIsValid(scan))
164
		elog(ERROR, "%s: cannot begin scan of pg_database.", command);
165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181

	/*
	 * 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.
	 */
	tup = heap_getnext(scan, 0, &buf);

	if (HeapTupleIsValid(tup))
	{
		dbtup = heap_copytuple(tup);
		ReleaseBuffer(buf);
	}
	else
		dbtup = tup;

	heap_endscan(scan);
	return (dbtup);
182 183 184
}

/*
185
 *	check_permissions() -- verify that the user is permitted to do this.
186
 *
187
 *	If the user is not allowed to carry out this operation, this routine
188
 *	elog(ERROR, ...)s, which will abort the xact.  As a side effect, the
189 190
 *	user's pg_user tuple OID is returned in userIdP and the target database's
 *	OID is returned in dbIdP.
191 192 193 194
 */

static void
check_permissions(char *command,
195
				  char *dbpath,
196
				  char *dbname,
197 198
				  Oid *dbIdP,
				  Oid *userIdP)
199
{
200 201 202 203 204 205 206 207
	Relation	dbrel;
	HeapTuple	dbtup,
				utup;
	Oid			dbowner = (Oid) 0;
	char		use_createdb;
	bool		dbfound;
	bool		use_super;
	char	   *userName;
208
	text	   *dbtext;
209
	char		path[MAXPGPATH + 1];
210 211 212 213

	userName = GetPgUserName();
	utup = SearchSysCacheTuple(USENAME, PointerGetDatum(userName),
							   0, 0, 0);
214 215 216
	*userIdP = ((Form_pg_shadow) GETSTRUCT(utup))->usesysid;
	use_super = ((Form_pg_shadow) GETSTRUCT(utup))->usesuper;
	use_createdb = ((Form_pg_shadow) GETSTRUCT(utup))->usecreatedb;
217 218 219 220

	/* Check to make sure user has permission to use createdb */
	if (!use_createdb)
	{
221
		elog(ERROR, "user \"%s\" is not allowed to create/destroy databases",
222 223 224 225 226 227
			 userName);
	}

	/* Make sure we are not mucking with the template database */
	if (!strcmp(dbname, "template1"))
	{
228
		elog(ERROR, "%s cannot be executed on the template database.", command);
229 230 231
	}

	/* Check to make sure database is not the currently open database */
232
	if (!strcmp(dbname, DatabaseName))
233
	{
234
		elog(ERROR, "%s cannot be executed on an open database", command);
235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260
	}

	/* 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)
	{
261
		dbowner = (Oid) heap_getattr(dbtup,
262 263 264 265
									 Anum_pg_database_datdba,
									 RelationGetTupleDescriptor(dbrel),
									 (char *) NULL);
		*dbIdP = dbtup->t_oid;
266
		dbtext = (text *) heap_getattr(dbtup,
267 268 269
									   Anum_pg_database_datpath,
									   RelationGetTupleDescriptor(dbrel),
									   (char *) NULL);
270

271 272
		strncpy(path, VARDATA(dbtext), (VARSIZE(dbtext) - VARHDRSZ));
		*(path + VARSIZE(dbtext) - VARHDRSZ) = '\0';
273 274 275 276 277 278 279 280 281 282 283 284 285 286 287
	}
	else
	{
		*dbIdP = InvalidOid;
	}

	heap_close(dbrel);

	/*
	 * Now be sure that the user is allowed to do this.
	 */

	if (dbfound && !strcmp(command, "createdb"))
	{

288
		elog(ERROR, "createdb: database %s already exists.", dbname);
289 290 291 292 293

	}
	else if (!dbfound && !strcmp(command, "destroydb"))
	{

294
		elog(ERROR, "destroydb: database %s does not exist.", dbname);
295 296 297 298 299 300

	}
	else if (dbfound && !strcmp(command, "destroydb")
			 && dbowner != *userIdP && use_super == false)
	{

301
		elog(ERROR, "%s: database %s is not owned by you.", command, dbname);
302 303

	}
304 305 306

	if (dbfound && !strcmp(command, "destroydb"))
		strcpy(dbpath, path);
307
}	/* check_permissions() */
308 309

/*
310
 *	stop_vacuum() -- stop the vacuum daemon on the database, if one is running.
311
 */
312
static void
313
stop_vacuum(char *dbpath, char *dbname)
314
{
315 316 317
	char		filename[256];
	FILE	   *fp;
	int			pid;
318

319 320 321
	if (strchr(dbpath, SEP_CHAR) != 0)
	{
		sprintf(filename, "%s%cbase%c%s%c%s.vacuum", DataDir, SEP_CHAR, SEP_CHAR,
322
				dbname, SEP_CHAR, dbname);
323 324 325 326 327 328
	}
	else
	{
		sprintf(filename, "%s%c%s.vacuum", dbpath, SEP_CHAR, dbname);
	}

329 330 331 332 333 334
	if ((fp = AllocateFile(filename, "r")) != NULL)
	{
		fscanf(fp, "%d", &pid);
		FreeFile(fp);
		if (kill(pid, SIGKILLDAEMON1) < 0)
		{
335
			elog(ERROR, "can't kill vacuum daemon (pid %d) on %s",
336 337
				 pid, dbname);
		}
338 339
	}
}