]> granicus.if.org Git - postgresql/blob - src/backend/commands/dbcommands.c
Restructure system-catalog index updating logic. Instead of having
[postgresql] / src / backend / commands / dbcommands.c
1 /*-------------------------------------------------------------------------
2  *
3  * dbcommands.c
4  *              Database management commands (create/drop database).
5  *
6  *
7  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
8  * Portions Copyright (c) 1994, Regents of the University of California
9  *
10  *
11  * IDENTIFICATION
12  *        $Header: /cvsroot/pgsql/src/backend/commands/dbcommands.c,v 1.98 2002/08/05 03:29:16 tgl Exp $
13  *
14  *-------------------------------------------------------------------------
15  */
16 #include "postgres.h"
17
18 #include <errno.h>
19 #include <fcntl.h>
20 #include <unistd.h>
21 #include <sys/stat.h>
22 #include <sys/types.h>
23
24 #include "access/heapam.h"
25 #include "catalog/catname.h"
26 #include "catalog/catalog.h"
27 #include "catalog/pg_database.h"
28 #include "catalog/pg_shadow.h"
29 #include "catalog/indexing.h"
30 #include "commands/comment.h"
31 #include "commands/dbcommands.h"
32 #include "miscadmin.h"
33 #include "storage/freespace.h"
34 #include "storage/sinval.h"
35 #include "utils/array.h"
36 #include "utils/builtins.h"
37 #include "utils/fmgroids.h"
38 #include "utils/guc.h"
39 #include "utils/lsyscache.h"
40 #include "utils/syscache.h"
41
42 #ifdef MULTIBYTE
43 #include "mb/pg_wchar.h"                /* encoding check */
44 #endif
45
46
47 /* non-export function prototypes */
48 static bool get_db_info(const char *name, Oid *dbIdP, int4 *ownerIdP,
49                         int *encodingP, bool *dbIsTemplateP, Oid *dbLastSysOidP,
50                         TransactionId *dbVacuumXidP, TransactionId *dbFrozenXidP,
51                         char *dbpath);
52 static bool have_createdb_privilege(void);
53 static char *resolve_alt_dbpath(const char *dbpath, Oid dboid);
54 static bool remove_dbdirs(const char *real_loc, const char *altloc);
55
56 /*
57  * CREATE DATABASE
58  */
59
60 void
61 createdb(const CreatedbStmt *stmt)
62 {
63         char       *nominal_loc;
64         char       *alt_loc;
65         char       *target_dir;
66         char            src_loc[MAXPGPATH];
67         char            buf[2 * MAXPGPATH + 100];
68         Oid                     src_dboid;
69         int4            src_owner;
70         int                     src_encoding;
71         bool            src_istemplate;
72         Oid                     src_lastsysoid;
73         TransactionId src_vacuumxid;
74         TransactionId src_frozenxid;
75         char            src_dbpath[MAXPGPATH];
76         Relation        pg_database_rel;
77         HeapTuple       tuple;
78         TupleDesc       pg_database_dsc;
79         Datum           new_record[Natts_pg_database];
80         char            new_record_nulls[Natts_pg_database];
81         Oid                     dboid;
82         int32           datdba;
83         List       *option;
84         DefElem    *downer = NULL;
85         DefElem    *dpath = NULL;
86         DefElem    *dtemplate = NULL;
87         DefElem    *dencoding = NULL;
88         char       *dbname = stmt->dbname;
89         char       *dbowner = NULL;
90         char       *dbpath = NULL;
91         char       *dbtemplate = NULL;
92         int                 encoding = -1;
93
94         /* Extract options from the statement node tree */
95         foreach(option, stmt->options)
96         {
97                 DefElem    *defel = (DefElem *) lfirst(option);
98
99                 if (strcmp(defel->defname, "owner") == 0)
100                 {
101                         if (downer)
102                                 elog(ERROR, "CREATE DATABASE: conflicting options");
103                         downer = defel;
104                 }
105                 else if (strcmp(defel->defname, "location") == 0)
106                 {
107                         if (dpath)
108                                 elog(ERROR, "CREATE DATABASE: conflicting options");
109                         dpath = defel;
110                 }
111                 else if (strcmp(defel->defname, "template") == 0)
112                 {
113                         if (dtemplate)
114                                 elog(ERROR, "CREATE DATABASE: conflicting options");
115                         dtemplate = defel;
116                 }
117                 else if (strcmp(defel->defname, "encoding") == 0)
118                 {
119                         if (dencoding)
120                                 elog(ERROR, "CREATE DATABASE: conflicting options");
121                         dencoding = defel;
122                 }
123                 else
124                         elog(ERROR, "CREATE DATABASE: option \"%s\" not recognized",
125                                  defel->defname);
126         }
127
128         if (downer)
129                 dbowner = strVal(downer->arg);
130         if (dpath)
131                 dbpath = strVal(dpath->arg);
132         if (dtemplate)
133                 dbtemplate = strVal(dtemplate->arg);
134         if (dencoding)
135                 encoding = intVal(dencoding->arg);
136
137         /* obtain sysid of proposed owner */
138         if (dbowner)
139                 datdba = get_usesysid(dbowner); /* will elog if no such user */
140         else
141                 datdba = GetUserId();
142
143         if (datdba == (int32) GetUserId())
144         {
145                 /* creating database for self: can be superuser or createdb */
146                 if (!superuser() && !have_createdb_privilege())
147                         elog(ERROR, "CREATE DATABASE: permission denied");
148         }
149         else
150         {
151                 /* creating database for someone else: must be superuser */
152                 /* note that the someone else need not have any permissions */
153                 if (!superuser())
154                         elog(ERROR, "CREATE DATABASE: permission denied");
155         }
156
157         /* don't call this in a transaction block */
158         if (IsTransactionBlock())
159                 elog(ERROR, "CREATE DATABASE: may not be called in a transaction block");
160
161         /*
162          * Check for db name conflict.  There is a race condition here, since
163          * another backend could create the same DB name before we commit.
164          * However, holding an exclusive lock on pg_database for the whole
165          * time we are copying the source database doesn't seem like a good
166          * idea, so accept possibility of race to create.  We will check again
167          * after we grab the exclusive lock.
168          */
169         if (get_db_info(dbname, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL))
170                 elog(ERROR, "CREATE DATABASE: database \"%s\" already exists", dbname);
171
172         /*
173          * Lookup database (template) to be cloned.
174          */
175         if (!dbtemplate)
176                 dbtemplate = "template1";               /* Default template database name */
177
178         if (!get_db_info(dbtemplate, &src_dboid, &src_owner, &src_encoding,
179                                          &src_istemplate, &src_lastsysoid,
180                                          &src_vacuumxid, &src_frozenxid,
181                                          src_dbpath))
182                 elog(ERROR, "CREATE DATABASE: template \"%s\" does not exist",
183                          dbtemplate);
184
185         /*
186          * Permission check: to copy a DB that's not marked datistemplate, you
187          * must be superuser or the owner thereof.
188          */
189         if (!src_istemplate)
190         {
191                 if (!superuser() && GetUserId() != src_owner )
192                         elog(ERROR, "CREATE DATABASE: permission to copy \"%s\" denied",
193                                  dbtemplate);
194         }
195
196         /*
197          * Determine physical path of source database
198          */
199         alt_loc = resolve_alt_dbpath(src_dbpath, src_dboid);
200         if (!alt_loc)
201                 alt_loc = GetDatabasePath(src_dboid);
202         strcpy(src_loc, alt_loc);
203
204         /*
205          * The source DB can't have any active backends, except this one
206          * (exception is to allow CREATE DB while connected to template1).
207          * Otherwise we might copy inconsistent data.  This check is not
208          * bulletproof, since someone might connect while we are copying...
209          */
210         if (DatabaseHasActiveBackends(src_dboid, true))
211                 elog(ERROR, "CREATE DATABASE: source database \"%s\" is being accessed by other users", dbtemplate);
212
213         /* If encoding is defaulted, use source's encoding */
214         if (encoding < 0)
215                 encoding = src_encoding;
216
217 #ifdef MULTIBYTE
218         /* Some encodings are client only */
219         if (!PG_VALID_BE_ENCODING(encoding))
220                 elog(ERROR, "CREATE DATABASE: invalid backend encoding");
221 #else
222         Assert(encoding == 0);          /* zero is PG_SQL_ASCII */
223 #endif
224
225         /*
226          * Preassign OID for pg_database tuple, so that we can compute db
227          * path.
228          */
229         dboid = newoid();
230
231         /*
232          * Compute nominal location (where we will try to access the
233          * database), and resolve alternate physical location if one is
234          * specified.
235          *
236          * If an alternate location is specified but is the same as the
237          * normal path, just drop the alternate-location spec (this seems
238          * friendlier than erroring out).  We must test this case to avoid
239          * creating a circular symlink below.
240          */
241         nominal_loc = GetDatabasePath(dboid);
242         alt_loc = resolve_alt_dbpath(dbpath, dboid);
243
244         if (alt_loc && strcmp(alt_loc, nominal_loc) == 0)
245         {
246                 alt_loc = NULL;
247                 dbpath = NULL;
248         }
249
250         if (strchr(nominal_loc, '\''))
251                 elog(ERROR, "database path may not contain single quotes");
252         if (alt_loc && strchr(alt_loc, '\''))
253                 elog(ERROR, "database path may not contain single quotes");
254         if (strchr(src_loc, '\''))
255                 elog(ERROR, "database path may not contain single quotes");
256         /* ... otherwise we'd be open to shell exploits below */
257
258         /*
259          * Force dirty buffers out to disk, to ensure source database is
260          * up-to-date for the copy.  (We really only need to flush buffers for
261          * the source database...)
262          */
263         BufferSync();
264
265         /*
266          * Close virtual file descriptors so the kernel has more available for
267          * the mkdir() and system() calls below.
268          */
269         closeAllVfds();
270
271         /*
272          * Check we can create the target directory --- but then remove it
273          * because we rely on cp(1) to create it for real.
274          */
275         target_dir = alt_loc ? alt_loc : nominal_loc;
276
277         if (mkdir(target_dir, S_IRWXU) != 0)
278                 elog(ERROR, "CREATE DATABASE: unable to create database directory '%s': %m",
279                          target_dir);
280         if (rmdir(target_dir) != 0)
281                 elog(ERROR, "CREATE DATABASE: unable to remove temp directory '%s': %m",
282                          target_dir);
283
284         /* Make the symlink, if needed */
285         if (alt_loc)
286         {
287                 if (symlink(alt_loc, nominal_loc) != 0)
288                         elog(ERROR, "CREATE DATABASE: could not link '%s' to '%s': %m",
289                                  nominal_loc, alt_loc);
290         }
291
292         /* Copy the template database to the new location */
293         snprintf(buf, sizeof(buf), "cp -r '%s' '%s'", src_loc, target_dir);
294
295         if (system(buf) != 0)
296         {
297                 if (remove_dbdirs(nominal_loc, alt_loc))
298                         elog(ERROR, "CREATE DATABASE: could not initialize database directory");
299                 else
300                         elog(ERROR, "CREATE DATABASE: could not initialize database directory; delete failed as well");
301         }
302
303         /*
304          * Now OK to grab exclusive lock on pg_database.
305          */
306         pg_database_rel = heap_openr(DatabaseRelationName, AccessExclusiveLock);
307
308         /* Check to see if someone else created same DB name meanwhile. */
309         if (get_db_info(dbname, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL))
310         {
311                 /* Don't hold lock while doing recursive remove */
312                 heap_close(pg_database_rel, AccessExclusiveLock);
313                 remove_dbdirs(nominal_loc, alt_loc);
314                 elog(ERROR, "CREATE DATABASE: database \"%s\" already exists", dbname);
315         }
316
317         /*
318          * Insert a new tuple into pg_database
319          */
320         pg_database_dsc = RelationGetDescr(pg_database_rel);
321
322         /* Form tuple */
323         MemSet(new_record, 0, sizeof(new_record));
324         MemSet(new_record_nulls, ' ', sizeof(new_record_nulls));
325
326         new_record[Anum_pg_database_datname - 1] =
327                 DirectFunctionCall1(namein, CStringGetDatum(dbname));
328         new_record[Anum_pg_database_datdba - 1] = Int32GetDatum(datdba);
329         new_record[Anum_pg_database_encoding - 1] = Int32GetDatum(encoding);
330         new_record[Anum_pg_database_datistemplate - 1] = BoolGetDatum(false);
331         new_record[Anum_pg_database_datallowconn - 1] = BoolGetDatum(true);
332         new_record[Anum_pg_database_datlastsysoid - 1] = ObjectIdGetDatum(src_lastsysoid);
333         new_record[Anum_pg_database_datvacuumxid - 1] = TransactionIdGetDatum(src_vacuumxid);
334         new_record[Anum_pg_database_datfrozenxid - 1] = TransactionIdGetDatum(src_frozenxid);
335         /* do not set datpath to null, GetRawDatabaseInfo won't cope */
336         new_record[Anum_pg_database_datpath - 1] =
337                 DirectFunctionCall1(textin, CStringGetDatum(dbpath ? dbpath : ""));
338
339         new_record_nulls[Anum_pg_database_datconfig - 1] = 'n';
340         new_record_nulls[Anum_pg_database_datacl - 1] = 'n';
341
342         tuple = heap_formtuple(pg_database_dsc, new_record, new_record_nulls);
343
344         AssertTupleDescHasOid(pg_database_dsc);
345         HeapTupleSetOid(tuple, dboid);          /* override heap_insert's OID
346                                                                                  * selection */
347
348         simple_heap_insert(pg_database_rel, tuple);
349
350         /* Update indexes */
351         CatalogUpdateIndexes(pg_database_rel, tuple);
352
353         /* Close pg_database, but keep lock till commit */
354         heap_close(pg_database_rel, NoLock);
355
356         /*
357          * Force dirty buffers out to disk, so that newly-connecting backends
358          * will see the new database in pg_database right away.  (They'll see
359          * an uncommitted tuple, but they don't care; see GetRawDatabaseInfo.)
360          */
361         BufferSync();
362 }
363
364
365 /*
366  * DROP DATABASE
367  */
368 void
369 dropdb(const char *dbname)
370 {
371         int4            db_owner;
372         bool            db_istemplate;
373         Oid                     db_id;
374         char       *alt_loc;
375         char       *nominal_loc;
376         char            dbpath[MAXPGPATH];
377         Relation        pgdbrel;
378         HeapScanDesc pgdbscan;
379         ScanKeyData key;
380         HeapTuple       tup;
381
382         AssertArg(dbname);
383
384         if (strcmp(dbname, DatabaseName) == 0)
385                 elog(ERROR, "DROP DATABASE: cannot be executed on the currently open database");
386
387         if (IsTransactionBlock())
388                 elog(ERROR, "DROP DATABASE: may not be called in a transaction block");
389
390         /*
391          * Obtain exclusive lock on pg_database.  We need this to ensure that
392          * no new backend starts up in the target database while we are
393          * deleting it.  (Actually, a new backend might still manage to start
394          * up, because it will read pg_database without any locking to
395          * discover the database's OID.  But it will detect its error in
396          * ReverifyMyDatabase and shut down before any serious damage is done.
397          * See postinit.c.)
398          */
399         pgdbrel = heap_openr(DatabaseRelationName, AccessExclusiveLock);
400
401         if (!get_db_info(dbname, &db_id, &db_owner, NULL,
402                                          &db_istemplate, NULL, NULL, NULL, dbpath))
403                 elog(ERROR, "DROP DATABASE: database \"%s\" does not exist", dbname);
404
405         if (GetUserId() != db_owner && !superuser())
406                 elog(ERROR, "DROP DATABASE: permission denied");
407
408         /*
409          * Disallow dropping a DB that is marked istemplate.  This is just to
410          * prevent people from accidentally dropping template0 or template1;
411          * they can do so if they're really determined ...
412          */
413         if (db_istemplate)
414                 elog(ERROR, "DROP DATABASE: database is marked as a template");
415
416         nominal_loc = GetDatabasePath(db_id);
417         alt_loc = resolve_alt_dbpath(dbpath, db_id);
418
419         /*
420          * Check for active backends in the target database.
421          */
422         if (DatabaseHasActiveBackends(db_id, false))
423                 elog(ERROR, "DROP DATABASE: database \"%s\" is being accessed by other users", dbname);
424
425         /*
426          * Find the database's tuple by OID (should be unique).
427          */
428         ScanKeyEntryInitialize(&key, 0, ObjectIdAttributeNumber,
429                                                    F_OIDEQ, ObjectIdGetDatum(db_id));
430
431         pgdbscan = heap_beginscan(pgdbrel, SnapshotNow, 1, &key);
432
433         tup = heap_getnext(pgdbscan, ForwardScanDirection);
434         if (!HeapTupleIsValid(tup))
435         {
436                 /*
437                  * This error should never come up since the existence of the
438                  * database is checked earlier
439                  */
440                 elog(ERROR, "DROP DATABASE: Database \"%s\" doesn't exist despite earlier reports to the contrary",
441                          dbname);
442         }
443
444         /* Remove the database's tuple from pg_database */
445         simple_heap_delete(pgdbrel, &tup->t_self);
446
447         heap_endscan(pgdbscan);
448
449         /*
450          * Delete any comments associated with the database
451          *
452          * NOTE: this is probably dead code since any such comments should have
453          * been in that database, not mine.
454          */
455         DeleteComments(db_id, RelationGetRelid(pgdbrel), 0);
456
457         /*
458          * Close pg_database, but keep exclusive lock till commit to ensure
459          * that any new backend scanning pg_database will see the tuple dead.
460          */
461         heap_close(pgdbrel, NoLock);
462
463         /*
464          * Drop pages for this database that are in the shared buffer cache.
465          * This is important to ensure that no remaining backend tries to
466          * write out a dirty buffer to the dead database later...
467          */
468         DropBuffers(db_id);
469
470         /*
471          * Also, clean out any entries in the shared free space map.
472          */
473         FreeSpaceMapForgetDatabase(db_id);
474
475         /*
476          * Remove the database's subdirectory and everything in it.
477          */
478         remove_dbdirs(nominal_loc, alt_loc);
479
480         /*
481          * Force dirty buffers out to disk, so that newly-connecting backends
482          * will see the database tuple marked dead in pg_database right away.
483          * (They'll see an uncommitted deletion, but they don't care; see
484          * GetRawDatabaseInfo.)
485          */
486         BufferSync();
487 }
488
489
490
491 /*
492  * ALTER DATABASE name SET ...
493  */
494 void
495 AlterDatabaseSet(AlterDatabaseSetStmt *stmt)
496 {
497         char       *valuestr;
498         HeapTuple       tuple,
499                                 newtuple;
500         Relation        rel;
501         ScanKeyData     scankey;
502         HeapScanDesc scan;
503         Datum           repl_val[Natts_pg_database];
504         char            repl_null[Natts_pg_database];
505         char            repl_repl[Natts_pg_database];
506
507         valuestr = flatten_set_variable_args(stmt->variable, stmt->value);
508
509         rel = heap_openr(DatabaseRelationName, RowExclusiveLock);
510         ScanKeyEntryInitialize(&scankey, 0, Anum_pg_database_datname,
511                                                    F_NAMEEQ, NameGetDatum(stmt->dbname));
512         scan = heap_beginscan(rel, SnapshotNow, 1, &scankey);
513         tuple = heap_getnext(scan, ForwardScanDirection);
514         if (!HeapTupleIsValid(tuple))
515                 elog(ERROR, "database \"%s\" does not exist", stmt->dbname);
516
517         if (!(superuser()
518                   || ((Form_pg_database) GETSTRUCT(tuple))->datdba == GetUserId()))
519                 elog(ERROR, "permission denied");
520
521         MemSet(repl_repl, ' ', sizeof(repl_repl));
522         repl_repl[Anum_pg_database_datconfig-1] = 'r';
523
524         if (strcmp(stmt->variable, "all")==0 && valuestr == NULL)
525         {
526                 /* RESET ALL */
527                 repl_null[Anum_pg_database_datconfig-1] = 'n';
528                 repl_val[Anum_pg_database_datconfig-1] = (Datum) 0;
529         }
530         else
531         {
532                 Datum datum;
533                 bool isnull;
534                 ArrayType *a;
535
536                 repl_null[Anum_pg_database_datconfig-1] = ' ';
537
538                 datum = heap_getattr(tuple, Anum_pg_database_datconfig,
539                                                          RelationGetDescr(rel), &isnull);
540
541                 a = isnull ? ((ArrayType *) NULL) : DatumGetArrayTypeP(datum);
542
543                 if (valuestr)
544                         a = GUCArrayAdd(a, stmt->variable, valuestr);
545                 else
546                         a = GUCArrayDelete(a, stmt->variable);
547
548                 repl_val[Anum_pg_database_datconfig-1] = PointerGetDatum(a);
549         }
550
551         newtuple = heap_modifytuple(tuple, rel, repl_val, repl_null, repl_repl);
552         simple_heap_update(rel, &tuple->t_self, newtuple);
553
554         /* Update indexes */
555         CatalogUpdateIndexes(rel, newtuple);
556
557         heap_endscan(scan);
558         heap_close(rel, RowExclusiveLock);
559 }
560
561
562
563 /*
564  * Helper functions
565  */
566
567 static bool
568 get_db_info(const char *name, Oid *dbIdP, int4 *ownerIdP,
569                         int *encodingP, bool *dbIsTemplateP, Oid *dbLastSysOidP,
570                         TransactionId *dbVacuumXidP, TransactionId *dbFrozenXidP,
571                         char *dbpath)
572 {
573         Relation        relation;
574         ScanKeyData scanKey;
575         HeapScanDesc scan;
576         HeapTuple       tuple;
577         bool            gottuple;
578
579         AssertArg(name);
580
581         /* Caller may wish to grab a better lock on pg_database beforehand... */
582         relation = heap_openr(DatabaseRelationName, AccessShareLock);
583
584         ScanKeyEntryInitialize(&scanKey, 0, Anum_pg_database_datname,
585                                                    F_NAMEEQ, NameGetDatum(name));
586
587         scan = heap_beginscan(relation, SnapshotNow, 1, &scanKey);
588
589         tuple = heap_getnext(scan, ForwardScanDirection);
590
591         gottuple = HeapTupleIsValid(tuple);
592         if (gottuple)
593         {
594                 Form_pg_database dbform = (Form_pg_database) GETSTRUCT(tuple);
595
596                 /* oid of the database */
597                 if (dbIdP)
598                 {
599                         AssertTupleDescHasOid(relation->rd_att);
600                         *dbIdP = HeapTupleGetOid(tuple);
601                 }
602                 /* sysid of the owner */
603                 if (ownerIdP)
604                         *ownerIdP = dbform->datdba;
605                 /* multibyte encoding */
606                 if (encodingP)
607                         *encodingP = dbform->encoding;
608                 /* allowed as template? */
609                 if (dbIsTemplateP)
610                         *dbIsTemplateP = dbform->datistemplate;
611                 /* last system OID used in database */
612                 if (dbLastSysOidP)
613                         *dbLastSysOidP = dbform->datlastsysoid;
614                 /* limit of vacuumed XIDs */
615                 if (dbVacuumXidP)
616                         *dbVacuumXidP = dbform->datvacuumxid;
617                 /* limit of frozen XIDs */
618                 if (dbFrozenXidP)
619                         *dbFrozenXidP = dbform->datfrozenxid;
620                 /* database path (as registered in pg_database) */
621                 if (dbpath)
622                 {
623                         Datum           datum;
624                         bool            isnull;
625
626                         datum = heap_getattr(tuple,
627                                                                  Anum_pg_database_datpath,
628                                                                  RelationGetDescr(relation),
629                                                                  &isnull);
630                         if (!isnull)
631                         {
632                                 text       *pathtext = DatumGetTextP(datum);
633                                 int                     pathlen = VARSIZE(pathtext) - VARHDRSZ;
634
635                                 Assert(pathlen >= 0 && pathlen < MAXPGPATH);
636                                 strncpy(dbpath, VARDATA(pathtext), pathlen);
637                                 *(dbpath + pathlen) = '\0';
638                         }
639                         else
640                                 strcpy(dbpath, "");
641                 }
642         }
643
644         heap_endscan(scan);
645         heap_close(relation, AccessShareLock);
646
647         return gottuple;
648 }
649
650 static bool
651 have_createdb_privilege(void)
652 {
653         HeapTuple       utup;
654         bool            retval;
655
656         utup = SearchSysCache(SHADOWSYSID,
657                                                   ObjectIdGetDatum(GetUserId()),
658                                                   0, 0, 0);
659
660         if (!HeapTupleIsValid(utup))
661                 retval = false;
662         else
663                 retval = ((Form_pg_shadow) GETSTRUCT(utup))->usecreatedb;
664
665         ReleaseSysCache(utup);
666
667         return retval;
668 }
669
670
671 static char *
672 resolve_alt_dbpath(const char *dbpath, Oid dboid)
673 {
674         const char *prefix;
675         char       *ret;
676         size_t          len;
677
678         if (dbpath == NULL || dbpath[0] == '\0')
679                 return NULL;
680
681         if (strchr(dbpath, '/'))
682         {
683                 if (dbpath[0] != '/')
684                         elog(ERROR, "Relative paths are not allowed as database locations");
685 #ifndef ALLOW_ABSOLUTE_DBPATHS
686                 elog(ERROR, "Absolute paths are not allowed as database locations");
687 #endif
688                 prefix = dbpath;
689         }
690         else
691         {
692                 /* must be environment variable */
693                 char       *var = getenv(dbpath);
694
695                 if (!var)
696                         elog(ERROR, "Postmaster environment variable '%s' not set", dbpath);
697                 if (var[0] != '/')
698                         elog(ERROR, "Postmaster environment variable '%s' must be absolute path", dbpath);
699                 prefix = var;
700         }
701
702         len = strlen(prefix) + 6 + sizeof(Oid) * 8 + 1;
703         if (len >= MAXPGPATH - 100)
704                 elog(ERROR, "Alternate path is too long");
705
706         ret = palloc(len);
707         snprintf(ret, len, "%s/base/%u", prefix, dboid);
708
709         return ret;
710 }
711
712
713 static bool
714 remove_dbdirs(const char *nominal_loc, const char *alt_loc)
715 {
716         const char *target_dir;
717         char            buf[MAXPGPATH + 100];
718         bool            success = true;
719
720         target_dir = alt_loc ? alt_loc : nominal_loc;
721
722         /*
723          * Close virtual file descriptors so the kernel has more available for
724          * the system() call below.
725          */
726         closeAllVfds();
727
728         if (alt_loc)
729         {
730                 /* remove symlink */
731                 if (unlink(nominal_loc) != 0)
732                 {
733                         elog(WARNING, "could not remove '%s': %m", nominal_loc);
734                         success = false;
735                 }
736         }
737
738         snprintf(buf, sizeof(buf), "rm -rf '%s'", target_dir);
739
740         if (system(buf) != 0)
741         {
742                 elog(WARNING, "database directory '%s' could not be removed",
743                          target_dir);
744                 success = false;
745         }
746
747         return success;
748 }