]> granicus.if.org Git - postgresql/blob - src/backend/commands/dbcommands.c
Fixed everything in and surrounding createdb and dropdb to make it more
[postgresql] / 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.49 2000/01/13 18:26:05 petere Exp $
11  *
12  *-------------------------------------------------------------------------
13  */
14 #include "postgres.h"
15 #include "commands/dbcommands.h"
16
17 #include <errno.h>
18 #include <fcntl.h>
19 #include <stdlib.h>
20 #include <string.h>
21 #include <unistd.h>
22 #include <sys/stat.h>
23 #include <sys/types.h>
24
25 #include "access/heapam.h"
26 #include "access/htup.h"
27 #include "access/skey.h"
28 #include "access/xact.h"
29 #include "catalog/catname.h"
30 #include "catalog/indexing.h"
31 #include "catalog/pg_database.h"
32 #include "catalog/pg_shadow.h"
33 #include "commands/comment.h"
34 #include "miscadmin.h"
35 #include "storage/bufmgr.h"        /* for DropBuffers */
36 #include "storage/fd.h"            /* for closeAllVfds */
37 #include "storage/sinval.h"        /* for DatabaseHasActiveBackends */
38 #include "utils/builtins.h"
39 #include "utils/elog.h"
40 #include "utils/palloc.h"
41 #include "utils/rel.h"
42 #include "utils/syscache.h"
43
44
45 /* non-export function prototypes */
46 static bool
47 get_user_info(const char *name, int4 *use_sysid, bool *use_super, bool *use_createdb);
48
49 static bool
50 get_db_info(const char *name, char *dbpath, Oid *dbIdP, int4 *ownerIdP);
51
52
53
54 /*
55  * CREATE DATABASE
56  */
57
58 void
59 createdb(const char *dbname, const char *dbpath, int encoding)
60 {
61         char            buf[2 * MAXPGPATH + 100];
62         char       *loc;
63         int4            user_id;
64     bool        use_super, use_createdb;
65
66         Relation        pg_database_rel;
67         HeapTuple       tuple;
68     TupleDesc   pg_database_dsc;
69
70     Datum       new_record[Natts_pg_database];
71     char        new_record_nulls[Natts_pg_database] = { ' ', ' ', ' ', ' ' };
72
73
74     if (!get_user_info(GetPgUserName(), &user_id, &use_super, &use_createdb))
75         elog(ERROR, "Current user name is invalid.");
76
77     if (!use_createdb && !use_super)
78         elog(ERROR, "CREATE DATABASE: Permission denied.");
79
80     if (get_db_info(dbname, NULL, NULL, NULL))
81         elog(ERROR, "CREATE DATABASE: Database \"%s\" already exists.", dbname);
82
83         /* close virtual file descriptors so the kernel has more available for
84        the system() calls */
85         closeAllVfds();
86
87         /* Generate directory name for the new database */
88     if (dbpath == NULL)
89         dbpath = dbname;
90
91         loc = ExpandDatabasePath(dbpath);
92
93         if (loc == NULL)
94                 elog(ERROR,
95              "The database path '%s' is invalid. "
96                          "This may be due to a character that is not allowed or because the chosen "
97              "path isn't permitted for databases.", dbpath);
98
99     /* don't call this in a transaction block */
100         if (IsTransactionBlock())
101         elog(ERROR, "CREATE DATABASE: May not be called in a transaction block.");
102     else            
103                 BeginTransactionBlock();
104
105     /*
106      * Insert a new tuple into pg_database
107      */
108         pg_database_rel = heap_openr(DatabaseRelationName, AccessExclusiveLock);
109         pg_database_dsc = RelationGetDescr(pg_database_rel);
110
111     /* Form tuple */
112     new_record[Anum_pg_database_datname-1] = NameGetDatum(dbname);
113     new_record[Anum_pg_database_datdba-1] = Int32GetDatum(user_id);
114     new_record[Anum_pg_database_encoding-1] = Int32GetDatum(encoding);
115     new_record[Anum_pg_database_datpath-1] = PointerGetDatum(textin((char *)dbpath));
116
117     tuple = heap_formtuple(pg_database_dsc, new_record, new_record_nulls);
118
119     /*
120      * Update table
121      */
122     heap_insert(pg_database_rel, tuple);
123
124     /*
125      * Update indexes (there aren't any currently)
126      */
127 #ifdef Num_pg_database_indices
128     if (RelationGetForm(pg_database_rel)->relhasindex) {
129         Relation idescs[Num_pg_database_indices];
130       
131         CatalogOpenIndices(Num_pg_database_indices, 
132                            Name_pg_database_indices, idescs);
133         CatalogIndexInsert(idescs, Num_pg_database_indices, pg_database_rel, 
134                            tuple);
135         CatalogCloseIndices(Num_pg_database_indices, idescs);
136     }
137 #endif
138
139         heap_close(pg_database_rel, NoLock);
140
141     /* Copy the template database to the new location */
142
143         if (mkdir(loc, S_IRWXU) != 0) {
144                 UserAbortTransactionBlock();
145                 elog(ERROR, "CREATE DATABASE: Unable to create database directory '%s': %s", loc, strerror(errno));
146     }
147
148         snprintf(buf, sizeof(buf), "cp %s%cbase%ctemplate1%c* '%s'",
149                          DataDir, SEP_CHAR, SEP_CHAR, SEP_CHAR, loc);
150         if (system(buf) != 0) {
151         int ret;
152         snprintf(buf, sizeof(buf), "rm -rf '%s'", loc);
153         ret = system(buf);
154                 UserAbortTransactionBlock();
155         if (ret == 0)
156             elog(ERROR, "CREATE DATABASE: Could not initialize database directory.");
157         else
158             elog(ERROR, "CREATE DATABASE: Could not initialize database directory. Delete failed as well.");
159     }
160
161         if (IsTransactionBlock())
162                 EndTransactionBlock();
163 }
164
165
166
167 /*
168  * DROP DATABASE
169  */
170
171 void
172 dropdb(const char *dbname)
173 {
174         int4            user_id, db_owner;
175     bool        use_super;
176         Oid                     db_id;
177         char       *path,
178                                 dbpath[MAXPGPATH],
179                                 buf[MAXPGPATH + 100];
180
181         Relation        pgdbrel;
182         HeapScanDesc pgdbscan;
183         ScanKeyData     key;
184         HeapTuple       tup;
185
186     AssertArg(dbname);
187
188         if (strcmp(dbname, "template1") == 0)
189                 elog(ERROR, "DROP DATABASE: May not be executed on the template database.");
190
191         if (strcmp(dbname, DatabaseName) == 0)
192                 elog(ERROR, "DROP DATABASE: Cannot be executed on the currently open database.");
193
194     if (!get_user_info(GetPgUserName(), &user_id, &use_super, NULL))
195         elog(ERROR, "Current user name is invalid.");
196
197     if (!get_db_info(dbname, dbpath, &db_id, &db_owner))
198         elog(ERROR, "DROP DATABASE: Database \"%s\" does not exist.", dbname);
199
200     if (user_id != db_owner && !use_super)
201         elog(ERROR, "DROP DATABASE: Permission denied.");
202
203         /* close virtual file descriptors so the kernel has more available for
204        the system() calls */
205         closeAllVfds();
206
207         path = ExpandDatabasePath(dbpath);
208         if (path == NULL)
209                 elog(ERROR,
210              "The database path '%s' is invalid. "
211                          "This may be due to a character that is not allowed or because the chosen "
212              "path isn't permitted for databases.", path);
213
214     /* don't call this in a transaction block */
215         if (IsTransactionBlock())
216         elog(ERROR, "DROP DATABASE: May not be called in a transaction block.");
217     else            
218                 BeginTransactionBlock();
219
220         /*
221          * Obtain exclusive lock on pg_database.  We need this to ensure
222          * that no new backend starts up in the target database while we
223          * are deleting it.  (Actually, a new backend might still manage to
224          * start up, because it will read pg_database without any locking
225          * to discover the database's OID.  But it will detect its error
226          * in ReverifyMyDatabase and shut down before any serious damage
227          * is done.  See postinit.c.)
228          */
229         pgdbrel = heap_openr(DatabaseRelationName, AccessExclusiveLock);
230
231         /*
232          * Check for active backends in the target database.
233          */
234         if (DatabaseHasActiveBackends(db_id)) {
235                 heap_close(pgdbrel, AccessExclusiveLock);
236         UserAbortTransactionBlock();
237                 elog(ERROR, "DROP DATABASE: Database \"%s\" is being accessed by other users.", dbname);
238     }
239
240         /*
241          * Find the database's tuple by OID (should be unique, we trust).
242          */
243         ScanKeyEntryInitialize(&key, 0, ObjectIdAttributeNumber,
244                                                    F_OIDEQ, ObjectIdGetDatum(db_id));
245
246         pgdbscan = heap_beginscan(pgdbrel, 0, SnapshotNow, 1, &key);
247
248         tup = heap_getnext(pgdbscan, 0);
249         if (!HeapTupleIsValid(tup))
250         {
251                 heap_close(pgdbrel, AccessExclusiveLock);
252         UserAbortTransactionBlock();
253         /* This error should never come up since the existence of the
254            database is checked earlier */
255                 elog(ERROR, "DROP DATABASE: Database \"%s\" doesn't exist despite earlier reports to the contrary.",
256                          dbname);
257         }
258
259         /* Delete any comments associated with the database */
260         DeleteComments(db_id);
261
262         /* Remove the database's tuple from pg_database */
263         heap_delete(pgdbrel, &tup->t_self, NULL);
264
265         heap_endscan(pgdbscan);
266
267         /*
268          * Close pg_database, but keep exclusive lock till commit to ensure
269          * that any new backend scanning pg_database will see the tuple dead.
270          */
271         heap_close(pgdbrel, NoLock);
272
273         /*
274          * Drop pages for this database that are in the shared buffer cache.
275          * This is important to ensure that no remaining backend tries to
276          * write out a dirty buffer to the dead database later...
277          */
278         DropBuffers(db_id);
279
280         /*
281          * Remove the database's subdirectory and everything in it.
282          */
283         snprintf(buf, sizeof(buf), "rm -rf '%s'", path);
284         if (system(buf)!=0)
285         elog(NOTICE, "DROP DATABASE: The database directory '%s' could not be removed.", path);
286
287         if (IsTransactionBlock())
288                 EndTransactionBlock();
289 }
290
291
292
293 /*
294  * Helper functions
295  */
296
297 static bool
298 get_db_info(const char *name, char *dbpath, Oid *dbIdP, int4 *ownerIdP)
299 {
300         Relation        relation;
301         HeapTuple       tuple;
302         ScanKeyData scanKey;
303     HeapScanDesc scan;
304
305     AssertArg(name);
306
307         relation = heap_openr(DatabaseRelationName, AccessExclusiveLock/*???*/);
308
309         ScanKeyEntryInitialize(&scanKey, 0, Anum_pg_database_datname,
310                                                    F_NAMEEQ, NameGetDatum(name));
311
312         scan = heap_beginscan(relation, 0, SnapshotNow, 1, &scanKey);
313         if (!HeapScanIsValid(scan))
314                 elog(ERROR, "Cannot begin scan of %s.", DatabaseRelationName);
315
316         tuple = heap_getnext(scan, 0);
317
318         if (HeapTupleIsValid(tuple))
319         {
320         text       *tmptext;
321         bool        isnull;
322     
323         /* oid of the database */
324         if (dbIdP)
325             *dbIdP = tuple->t_data->t_oid;
326         /* uid of the owner */
327         if (ownerIdP)
328         {
329             *ownerIdP = (int4) heap_getattr(tuple,
330                                             Anum_pg_database_datdba,
331                                             RelationGetDescr(relation),
332                                             &isnull);
333             if (isnull)
334                 *ownerIdP = -1; /* hopefully no one has that id already ;) */
335         }
336         /* database path (as registered in pg_database) */
337         if (dbpath)
338         {
339             tmptext = (text *) heap_getattr(tuple,
340                                             Anum_pg_database_datpath,
341                                             RelationGetDescr(relation),
342                                             &isnull);
343
344             if (!isnull)
345             {
346                 Assert(VARSIZE(tmptext) - VARHDRSZ < MAXPGPATH);
347
348                 strncpy(dbpath, VARDATA(tmptext), VARSIZE(tmptext) - VARHDRSZ);
349                 *(dbpath + VARSIZE(tmptext) - VARHDRSZ) = '\0';
350             }
351             else
352                 strcpy(dbpath, "");
353         }
354         }
355         else
356     {
357         if (dbIdP)
358             *dbIdP = InvalidOid;
359     }
360
361         heap_endscan(scan);
362
363         /* We will keep the lock on the relation until end of transaction. */
364         heap_close(relation, NoLock);
365
366     return HeapTupleIsValid(tuple);
367 }
368
369
370
371 static bool
372 get_user_info(const char * name, int4 *use_sysid, bool *use_super, bool *use_createdb)
373 {
374     HeapTuple   utup;
375
376         AssertArg(name);
377         utup = SearchSysCacheTuple(SHADOWNAME,
378                                                            PointerGetDatum(name),
379                                                            0, 0, 0);
380
381     if (!HeapTupleIsValid(utup))
382         return false;
383
384     if (use_sysid)
385         *use_sysid =    ((Form_pg_shadow) GETSTRUCT(utup))->usesysid;
386     if (use_super)
387         *use_super =    ((Form_pg_shadow) GETSTRUCT(utup))->usesuper;
388     if (use_createdb)
389         *use_createdb = ((Form_pg_shadow) GETSTRUCT(utup))->usecreatedb;
390
391     return true;
392 }