OSDN Git Service

Use Snapshot in heap access methods.
[pg-rex/syncrep.git] / src / backend / commands / dbcommands.c
1 /*-------------------------------------------------------------------------
2  *
3  * dbcommands.c--
4  *
5  *
6  * Copyright (c) 1994, Regents of the University of California
7  *
8  *
9  * IDENTIFICATION
10  *        $Header: /cvsroot/pgsql/src/backend/commands/dbcommands.c,v 1.18 1998/07/27 19:37:52 vadim 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
21 #include "access/heapam.h"
22 #include "access/htup.h"
23 #include "access/relscan.h"
24 #include "catalog/catname.h"
25 #include "catalog/pg_database.h"
26 #include "catalog/pg_shadow.h"
27 #include "commands/dbcommands.h"
28 #include "fmgr.h"
29 #include "miscadmin.h"                  /* for DataDir */
30 #include "storage/bufmgr.h"
31 #include "storage/fd.h"
32 #include "storage/lmgr.h"
33 #include "tcop/tcopprot.h"
34 #include "utils/rel.h"
35 #include "utils/syscache.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         Oid                     db_id,
49                                 user_id;
50         char            buf[512];
51         char       *lp,
52                                 loc[512];
53
54         /*
55          * If this call returns, the database does not exist and we're allowed
56          * to create databases.
57          */
58         check_permissions("createdb", dbpath, dbname, &db_id, &user_id);
59
60         /* close virtual file descriptors so we can do system() calls */
61         closeAllVfds();
62
63         /* Now create directory for this new database */
64         if ((dbpath != NULL) && (strcmp(dbpath, dbname) != 0))
65         {
66                 if (*(dbpath + strlen(dbpath) - 1) == SEP_CHAR)
67                         *(dbpath + strlen(dbpath) - 1) = '\0';
68                 sprintf(loc, "%s%c%s", dbpath, SEP_CHAR, dbname);
69         }
70         else
71                 strcpy(loc, dbname);
72
73         lp = ExpandDatabasePath(loc);
74
75         if (lp == NULL)
76                 elog(ERROR, "Unable to locate path '%s'"
77                          "\n\tThis may be due to a missing environment variable"
78                          " in the server", loc);
79
80         if (mkdir(lp, S_IRWXU) != 0)
81                 elog(ERROR, "Unable to create database directory %s", lp);
82
83         sprintf(buf, "%s %s%cbase%ctemplate1%c* %s",
84                         COPY_CMD, DataDir, SEP_CHAR, SEP_CHAR, SEP_CHAR, lp);
85         system(buf);
86
87 #if FALSE
88         sprintf(buf, "insert into pg_database (datname, datdba, datpath) \
89                   values (\'%s\'::name, \'%d\'::oid, \'%s\'::text);",
90                         dbname, user_id, dbname);
91 #endif
92
93         sprintf(buf, "insert into pg_database (datname, datdba, datpath)"
94                         " values (\'%s\', \'%d\', \'%s\');", dbname, user_id, loc);
95
96         pg_exec_query(buf);
97 }
98
99 void
100 destroydb(char *dbname)
101 {
102         Oid                     user_id,
103                                 db_id;
104         char       *path;
105         char            dbpath[MAXPGPATH + 1];
106         char            buf[512];
107
108         /*
109          * If this call returns, the database exists and we're allowed to
110          * remove it.
111          */
112         check_permissions("destroydb", dbpath, dbname, &db_id, &user_id);
113
114         if (!OidIsValid(db_id))
115                 elog(FATAL, "impossible: pg_database instance with invalid OID.");
116
117         /* stop the vacuum daemon */
118         stop_vacuum(dbpath, dbname);
119
120         path = ExpandDatabasePath(dbpath);
121         if (path == NULL)
122                 elog(ERROR, "Unable to locate path '%s'"
123                          "\n\tThis may be due to a missing environment variable"
124                          " in the server", dbpath);
125
126         /*
127          * remove the pg_database tuple FIRST, this may fail due to
128          * permissions problems
129          */
130         sprintf(buf, "delete from pg_database where pg_database.oid = \'%d\'::oid",
131                         db_id);
132         pg_exec_query(buf);
133
134         /*
135          * remove the data directory. If the DELETE above failed, this will
136          * not be reached
137          */
138
139         sprintf(buf, "rm -r %s", path);
140         system(buf);
141
142         /* drop pages for this database that are in the shared buffer cache */
143         DropBuffers(db_id);
144 }
145
146 static HeapTuple
147 get_pg_dbtup(char *command, char *dbname, Relation dbrel)
148 {
149         HeapTuple       dbtup;
150         HeapTuple       tup;
151         Buffer          buf;
152         HeapScanDesc scan;
153         ScanKeyData scanKey;
154
155         ScanKeyEntryInitialize(&scanKey, 0, Anum_pg_database_datname,
156                                                    F_NAMEEQ, NameGetDatum(dbname));
157
158         scan = heap_beginscan(dbrel, 0, SnapshotNow, 1, &scanKey);
159         if (!HeapScanIsValid(scan))
160                 elog(ERROR, "%s: cannot begin scan of pg_database.", command);
161
162         /*
163          * since we want to return the tuple out of this proc, and we're going
164          * to close the relation, copy the tuple and return the copy.
165          */
166         tup = heap_getnext(scan, 0, &buf);
167
168         if (HeapTupleIsValid(tup))
169         {
170                 dbtup = heap_copytuple(tup);
171                 ReleaseBuffer(buf);
172         }
173         else
174                 dbtup = tup;
175
176         heap_endscan(scan);
177         return (dbtup);
178 }
179
180 /*
181  *      check_permissions() -- verify that the user is permitted to do this.
182  *
183  *      If the user is not allowed to carry out this operation, this routine
184  *      elog(ERROR, ...)s, which will abort the xact.  As a side effect, the
185  *      user's pg_user tuple OID is returned in userIdP and the target database's
186  *      OID is returned in dbIdP.
187  */
188
189 static void
190 check_permissions(char *command,
191                                   char *dbpath,
192                                   char *dbname,
193                                   Oid *dbIdP,
194                                   Oid *userIdP)
195 {
196         Relation        dbrel;
197         HeapTuple       dbtup,
198                                 utup;
199         Oid                     dbowner = (Oid) 0;
200         char            use_createdb;
201         bool            dbfound;
202         bool            use_super;
203         char       *userName;
204         text       *dbtext;
205         char            path[MAXPGPATH + 1];
206
207         userName = GetPgUserName();
208         utup = SearchSysCacheTuple(USENAME, PointerGetDatum(userName),
209                                                            0, 0, 0);
210         *userIdP = ((Form_pg_shadow) GETSTRUCT(utup))->usesysid;
211         use_super = ((Form_pg_shadow) GETSTRUCT(utup))->usesuper;
212         use_createdb = ((Form_pg_shadow) GETSTRUCT(utup))->usecreatedb;
213
214         /* Check to make sure user has permission to use createdb */
215         if (!use_createdb)
216         {
217                 elog(ERROR, "user \"%s\" is not allowed to create/destroy databases",
218                          userName);
219         }
220
221         /* Make sure we are not mucking with the template database */
222         if (!strcmp(dbname, "template1"))
223                 elog(ERROR, "%s cannot be executed on the template database.", command);
224
225         /* Check to make sure database is not the currently open database */
226         if (!strcmp(dbname, DatabaseName))
227                 elog(ERROR, "%s cannot be executed on an open database", command);
228
229         /* Check to make sure database is owned by this user */
230
231         /*
232          * need the reldesc to get the database owner out of dbtup and to set
233          * a write lock on it.
234          */
235         dbrel = heap_openr(DatabaseRelationName);
236
237         if (!RelationIsValid(dbrel))
238                 elog(FATAL, "%s: cannot open relation \"%-.*s\"",
239                          command, DatabaseRelationName);
240
241         /*
242          * Acquire a write lock on pg_database from the beginning to avoid
243          * upgrading a read lock to a write lock.  Upgrading causes long
244          * delays when multiple 'createdb's or 'destroydb's are run simult.
245          * -mer 7/3/91
246          */
247         RelationSetLockForWrite(dbrel);
248         dbtup = get_pg_dbtup(command, dbname, dbrel);
249         dbfound = HeapTupleIsValid(dbtup);
250
251         if (dbfound)
252         {
253                 dbowner = (Oid) heap_getattr(dbtup,
254                                                                          Anum_pg_database_datdba,
255                                                                          RelationGetTupleDescriptor(dbrel),
256                                                                          (char *) NULL);
257                 *dbIdP = dbtup->t_oid;
258                 dbtext = (text *) heap_getattr(dbtup,
259                                                                            Anum_pg_database_datpath,
260                                                                            RelationGetTupleDescriptor(dbrel),
261                                                                            (char *) NULL);
262
263                 strncpy(path, VARDATA(dbtext), (VARSIZE(dbtext) - VARHDRSZ));
264                 *(path + VARSIZE(dbtext) - VARHDRSZ) = '\0';
265         }
266         else
267                 *dbIdP = InvalidOid;
268
269         heap_close(dbrel);
270
271         /*
272          * Now be sure that the user is allowed to do this.
273          */
274
275         if (dbfound && !strcmp(command, "createdb"))
276         {
277
278                 elog(ERROR, "createdb: database %s already exists.", dbname);
279
280         }
281         else if (!dbfound && !strcmp(command, "destroydb"))
282         {
283
284                 elog(ERROR, "destroydb: database %s does not exist.", dbname);
285
286         }
287         else if (dbfound && !strcmp(command, "destroydb")
288                          && dbowner != *userIdP && use_super == false)
289         {
290
291                 elog(ERROR, "%s: database %s is not owned by you.", command, dbname);
292
293         }
294
295         if (dbfound && !strcmp(command, "destroydb"))
296                 strcpy(dbpath, path);
297 }       /* check_permissions() */
298
299 /*
300  *      stop_vacuum() -- stop the vacuum daemon on the database, if one is running.
301  */
302 static void
303 stop_vacuum(char *dbpath, char *dbname)
304 {
305         char            filename[256];
306         FILE       *fp;
307         int                     pid;
308
309         if (strchr(dbpath, SEP_CHAR) != 0)
310         {
311                 sprintf(filename, "%s%cbase%c%s%c%s.vacuum", DataDir, SEP_CHAR, SEP_CHAR,
312                                 dbname, SEP_CHAR, dbname);
313         }
314         else
315                 sprintf(filename, "%s%c%s.vacuum", dbpath, SEP_CHAR, dbname);
316
317         if ((fp = AllocateFile(filename, "r")) != NULL)
318         {
319                 fscanf(fp, "%d", &pid);
320                 FreeFile(fp);
321                 if (kill(pid, SIGKILLDAEMON1) < 0)
322                 {
323                         elog(ERROR, "can't kill vacuum daemon (pid %d) on %s",
324                                  pid, dbname);
325                 }
326         }
327 }