Meanwhile, database names with single quotes in names don't work very well

at all, and because of shell quoting rules this can't be fixed, so I put
in error messages to that end.

Also, calling create or drop database in a transaction block is not so
good either, because the file system mysteriously refuses to roll back rm
calls on transaction aborts. :) So I put in checks to see if a transaction
is in progress and signal an error.

Also I put the whole call in a transaction of its own to be able to roll
back changes to pg_database in case the file system operations fail.

The alternative location issues I posted recently were untouched, awaiting
the outcome of that discussion. Other than that, this should be much more
fool-proof now.

The docs I cleaned up as well.

Peter Eisentraut                  Sernanders väg 10:115
This commit is contained in:
Bruce Momjian 1999-12-12 05:15:10 +00:00
parent 1ff0a475ee
commit 11023eb1f5
4 changed files with 236 additions and 79 deletions

View File

@ -71,7 +71,7 @@ MISC
* Fix btree to give a useful elog when key > 1/2 (page - overhead)
* -pg_dump should preserve primary key information
* plpgsql regression tests fail on BSD/OS
* database names with spaces fail
* -database names with spaces fail
* insert of 0.0 into DECIMAL(4,4) field fails
ENHANCEMENTS

View File

@ -1,5 +1,5 @@
<!--
$Header: /cvsroot/pgsql/doc/src/sgml/ref/create_database.sgml,v 1.10 1999/12/04 04:53:15 momjian Exp $
$Header: /cvsroot/pgsql/doc/src/sgml/ref/create_database.sgml,v 1.11 1999/12/12 05:15:09 momjian Exp $
Postgres documentation
-->
@ -20,7 +20,7 @@ Postgres documentation
</refnamediv>
<refsynopsisdiv>
<refsynopsisdivinfo>
<date>1999-07-20</date>
<date>1999-12-11</date>
</refsynopsisdivinfo>
<synopsis>
CREATE DATABASE <replaceable class="PARAMETER">name</replaceable> [ WITH LOCATION = '<replaceable class="parameter">dbpath</replaceable>' ]
@ -28,7 +28,7 @@ CREATE DATABASE <replaceable class="PARAMETER">name</replaceable> [ WITH LOCATIO
<refsect2 id="R2-SQL-CREATEDATABASE-1">
<refsect2info>
<date>1998-04-15</date>
<date>1999-12-11</date>
</refsect2info>
<title>
Inputs
@ -48,7 +48,8 @@ CREATE DATABASE <replaceable class="PARAMETER">name</replaceable> [ WITH LOCATIO
<term><replaceable class="parameter">dbpath</replaceable></term>
<listitem>
<para>
An alternate location for the new database. See below for caveats.
An alternate location where to store the new database in the filesystem.
See below for caveats.
</para>
</listitem>
</varlistentry>
@ -58,7 +59,7 @@ CREATE DATABASE <replaceable class="PARAMETER">name</replaceable> [ WITH LOCATIO
<refsect2 id="R2-SQL-CREATEDATABASE-2">
<refsect2info>
<date>1998-04-15</date>
<date>1999-12-11</date>
</refsect2info>
<title>
Outputs
@ -67,36 +68,81 @@ CREATE DATABASE <replaceable class="PARAMETER">name</replaceable> [ WITH LOCATIO
<variablelist>
<varlistentry>
<term><computeroutput>
CREATE DATABASE
</computeroutput></term>
<term><computeroutput>CREATE DATABASE</computeroutput></term>
<listitem>
<para>
Message returned if the command completes successfully.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><computeroutput>
WARN: createdb: database "<replaceable class="parameter">name</replaceable>" already exists.
</computeroutput></term>
<term><computeroutput>ERROR: user '<replaceable class="parameter">username</replaceable>' is not allowed to create/drop databases</computeroutput></term>
<listitem>
<para>
This occurs if <replaceable class="parameter">database</replaceable> specified already exists.
You must have the special CREATEDB privilege to create databases.
See <xref linkend="SQL-CREATEUSER" endterm="SQL-CREATEUSER-title">.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><computeroutput>
ERROR: Unable to create database directory <replaceable class="parameter">directory</replaceable>
</computeroutput></term>
<term><computeroutput>ERROR: createdb: database "<replaceable class="parameter">name</replaceable>" already exists</computeroutput></term>
<listitem>
<para>
There was a problem with creating the required directory; this operation will
need permissions for the <literal>postgres</literal> user on the specified location.
This occurs if a database with the <replaceable class="parameter">name</replaceable>
specified already exists.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><computeroutput>ERROR: Single quotes are not allowed in database names.</computeroutput></term>
<term><computeroutput>ERROR: Single quotes are not allowed in database paths.</computeroutput></term>
<listitem>
<para>
The database <replaceable class="parameter">name</replaceable> and
<replaceable class="parameter">dbpath</replaceable> cannot contain
single quotes. This is required so that the shell commands that
create the database directory can execute safely.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><computeroutput>ERROR: The path 'xxx' is invalid.</computeroutput></term>
<listitem>
<para>
The expansion of the specified <replaceable class="parameter">dbpath</replaceable>
(see below how) failed. Check the path you entered or make sure that the
environment variable you are referencing does exist.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><computeroutput>ERROR: createdb: May not be called in a transaction block.</computeroutput></term>
<listitem>
<para>
If you have an explicit transaction block in progress you cannot call
<command>CREATE DATABASE</command>. You must finish the transaction first.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><computeroutput>ERROR: Unable to create database directory 'xxx'.</computeroutput></term>
<term><computeroutput>ERROR: Could not initialize database directory.</computeroutput></term>
<listitem>
<para>
These are most likely related to insufficient permissions on the data
directory, a full disk, or other file system problems. The user under
which the database server is running, must have access to the location.
</para>
</listitem>
</varlistentry>
</variablelist>
</para>
</refsect2>
@ -104,29 +150,40 @@ ERROR: Unable to create database directory <replaceable class="parameter">direc
<refsect1 id="R1-SQL-CREATEDATABASE-1">
<refsect1info>
<date>1998-04-15</date>
<date>1999-12-11</date>
</refsect1info>
<title>
Description
</title>
<para>
<command>CREATE DATABASE</command> creates a new Postgres database.
The creator becomes the administrator of the new database.
<command>CREATE DATABASE</command> creates a new
<productname>PostgreSQL</productname> database.
The creator becomes the owner of the new database.
</para>
<para>
An alternate location can be specified as either an
environment variable known to the backend server
(e.g. '<envar>PGDATA2</envar>') or, if the server is built to
allow it, as an absolute path name
(e.g. '<filename>/usr/local/pgsql/data</filename>').
In either case, the location must be pre-configured
by <command>initlocation</command>.
An alternate location can be specified in order to,
for example, store the database on a different disk.
The path must have been prepared with the <xref
linkend="APP-INITLOCATION" endterm="APP-INITLOCATION-title">
command.
</para>
<para>
If the path contains a slash, the leading part is interpreted
as an environment variable, which must be known to the
server process. This way the database administrator can
exercise control over at which locations databases can be created.
(A customary choice is, e.g., '<envar>PGDATA2</envar>'.)
If the server is compiled with <literal>ALLOW_ABSOLUTE_DBPATHS</literal>
(not so by default), absolute path names, as identified by
a leading slash
(e.g. '<filename>/usr/local/pgsql/data</filename>'),
are allowed as well.
</para>
<refsect2 id="R2-SQL-CREATEDATABASE-3">
<refsect2info>
<date>1998-04-15</date>
<date>1999-12-11</date>
</refsect2info>
<title>
Notes
@ -136,7 +193,11 @@ ERROR: Unable to create database directory <replaceable class="parameter">direc
language extension.
</para>
<para>
Use <command>DROP DATABASE</command> to remove a database.
Use <xref linkend="SQL-DROPDATABASE" endterm="SQL-DROPDATABASE-title"> to remove a database.
</para>
<para>
The program <xref linkend="APP-CREATEDB" endterm="APP-CREATEDB-title"> is a
shell script wrapper around this command, provided for convenience.
</para>
<para>
@ -183,16 +244,16 @@ comment from Olly; response from Thomas...
<prompt>$</prompt> <userinput>initlocation ~/private_db</userinput>
<computeroutput>Creating Postgres database system directory /home/olly/private_db/base</computeroutput>
<prompt>$</prompt> <userinput>psql olly</userinput>
<computeroutput>Welcome to psql, the PostgreSQL interactive terminal.
<prompt>$</prompt> <userinput>psql olly</userinput>
<computeroutput>Welcome to psql, the PostgreSQL interactive terminal.
(Please type \copyright to see the distribution terms of PostgreSQL.)
Type \h for help with SQL commands,
\? for help on internal slash commands,
\q to quit,
\g or terminate with semicolon to execute query.
<prompt>olly=></prompt></computeroutput> <userinput>create database elsewhere with location = '/home/olly/private_db';</userinput>
<computeroutput>CREATE DATABASE</computeroutput>
<prompt>olly=></prompt></computeroutput> <userinput>CREATE DATABASE elsewhere WITH LOCATION = '/home/olly/private_db';</userinput>
<computeroutput>CREATE DATABASE</computeroutput>
</programlisting>
</para>
</refsect1>

View File

@ -1,5 +1,5 @@
<!--
$Header: /cvsroot/pgsql/doc/src/sgml/ref/drop_database.sgml,v 1.8 1999/12/04 04:53:15 momjian Exp $
$Header: /cvsroot/pgsql/doc/src/sgml/ref/drop_database.sgml,v 1.9 1999/12/12 05:15:09 momjian Exp $
Postgres documentation
-->
@ -15,12 +15,12 @@ Postgres documentation
DROP DATABASE
</refname>
<refpurpose>
Destroys an existing database
Removes an existing database
</refpurpose>
</refnamediv>
<refsynopsisdiv>
<refsynopsisdivinfo>
<date>1999-07-20</date>
<date>1999-12-11</date>
</refsynopsisdivinfo>
<synopsis>
DROP DATABASE <replaceable class="PARAMETER">name</replaceable>
@ -28,7 +28,7 @@ DROP DATABASE <replaceable class="PARAMETER">name</replaceable>
<refsect2 id="R2-SQL-DROPDATABASE-1">
<refsect2info>
<date>1998-04-15</date>
<date>1999-12-11</date>
</refsect2info>
<title>
Inputs
@ -49,7 +49,7 @@ DROP DATABASE <replaceable class="PARAMETER">name</replaceable>
<refsect2 id="R2-SQL-DROPDATABASE-2">
<refsect2info>
<date>1998-04-15</date>
<date>1999-12-11</date>
</refsect2info>
<title>
Outputs
@ -57,36 +57,86 @@ DROP DATABASE <replaceable class="PARAMETER">name</replaceable>
<para>
<variablelist>
<varlistentry>
<term><computeroutput>
DROP DATABASE
</computeroutput></term>
<term><computeroutput>DROP DATABASE</computeroutput></term>
<listitem>
<para>
This message is returned if the command is successful.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><computeroutput>
WARN: destroydb: database "<replaceable class="parameter">name</replaceable>" does not exist.
</computeroutput></term>
<term><computeroutput>ERROR: user '<replaceable class="parameter">username</replaceable>' is not allowed to create/drop databases</computeroutput></term>
<listitem>
<para>
You must have the special CREATEDB privilege to drop databases.
See <xref linkend="SQL-CREATEUSER" endterm="SQL-CREATEUSER-title">.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><computeroutput>ERROR: dropdb: cannot be executed on the template database</computeroutput></term>
<listitem>
<para>
The <literal>template1</literal> database cannot be removed. It's not in
your interest.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><computeroutput>ERROR: dropdb: cannot be executed on an open database</computeroutput></term>
<listitem>
<para>
You cannot be connected to the the database your are about to remove.
Instead, you could connect to <literal>template1</literal> or any other
database and run this command again.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><computeroutput>ERROR: dropdb: database '<replaceable class="parameter">name</replaceable>' does not exist</computeroutput></term>
<listitem>
<para>
This message occurs if the specified database does not exist.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><computeroutput>
ERROR: destroydb cannot be executed on an open database
</computeroutput></term>
<term><computeroutput>ERROR: dropdb: database '<replaceable class="parameter">name</replaceable>' is not owned by you</computeroutput></term>
<listitem>
<para>
This message occurs if the specified database does not exist.
You must be the owner of the database. Being the owner usually means that
you created it as well.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><computeroutput>ERROR: dropdb: May not be called in a transaction block.</computeroutput></term>
<listitem>
<para>
You must finish the transaction in progress before you can call this command.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><computeroutput>NOTICE: The database directory 'xxx' could not be removed.</computeroutput></term>
<listitem>
<para>
The database was dropped (unless other error messages came up), but the
directory where the data is stored could not be removed. You must delete
it manually.
</para>
</listitem>
</varlistentry>
</variablelist>
</para>
</refsect2>
@ -94,7 +144,7 @@ WARN: destroydb: database "<replaceable class="parameter">name</replaceable>" do
<refsect1 id="R1-SQL-DROPDATABASE-1">
<refsect1info>
<date>1998-04-15</date>
<date>1999-12-11</date>
</refsect1info>
<title>
Description
@ -102,23 +152,23 @@ WARN: destroydb: database "<replaceable class="parameter">name</replaceable>" do
<para>
<command>DROP DATABASE</command> removes the catalog entries for an existing
database and deletes the directory containing the data.
It can only be executed by the database administrator
(See the <command>CREATE DATABASE</command> command for details).
It can only be executed by the database owner (usually the user that created
it).
</para>
<refsect2 id="R2-SQL-DROPDATABASE-3">
<refsect2info>
<date>1998-04-15</date>
<date>1999-12-11</date>
</refsect2info>
<title>
Notes
</title>
<para>
This query cannot be executed while connected to the target
database. Thus, it might be more convenient to use
<xref linkend="app-dropdb" endterm="app-dropdb-title">
from the shell instead.
This command cannot be executed while connected to the target
database. Thus, it might be more convenient to use the shell
script <xref linkend="app-dropdb" endterm="app-dropdb-title">,
which is a wrapper around this command, instead.
</para>
<para>

View File

@ -7,7 +7,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/commands/dbcommands.c,v 1.46 1999/12/10 03:55:49 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/commands/dbcommands.c,v 1.47 1999/12/12 05:15:10 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@ -43,6 +43,12 @@ createdb(char *dbname, char *dbpath, int encoding, CommandDest dest)
char *lp,
loc[MAXPGPATH];
/* no single quotes in dbname */
if (strchr(dbname, '\'') != NULL)
elog(ERROR, "Single quotes are not allowed in database names.");
if (dbpath && strchr(dbpath, '\'') != NULL)
elog(ERROR, "Single quotes are not allowed in database paths.");
/*
* If this call returns, the database does not exist and we're allowed
* to create databases.
@ -52,7 +58,7 @@ createdb(char *dbname, char *dbpath, int encoding, CommandDest dest)
/* close virtual file descriptors so we can do system() calls */
closeAllVfds();
/* Now create directory for this new database */
/* Make directory name for this new database */
if ((dbpath != NULL) && (strcmp(dbpath, dbname) != 0))
{
if (*(dbpath + strlen(dbpath) - 1) == SEP_CHAR)
@ -65,25 +71,45 @@ createdb(char *dbname, char *dbpath, int encoding, CommandDest dest)
lp = ExpandDatabasePath(loc);
if (lp == NULL)
elog(ERROR, "Unable to locate path '%s'"
"\n\tThis may be due to a missing environment variable"
" in the server", loc);
elog(ERROR, "The path '%s' is invalid.\n"
"This may be due to a missing environment variable"
" on the server.", loc);
if (mkdir(lp, S_IRWXU) != 0)
elog(ERROR, "Unable to create database directory '%s'", lp);
/* no single quotes in expanded path */
if (strchr(lp, '\'') != NULL)
elog(ERROR, "Single quotes are not allowed in database paths.");
/* don't call this in a transaction block */
if (IsTransactionBlock())
elog(ERROR, "createdb: May not be called in a transaction block.");
else
BeginTransactionBlock();
snprintf(buf, sizeof(buf),
"INSERT INTO pg_database (datname, datdba, encoding, datpath)"
" VALUES ('%s', '%d', '%d', '%s')", dbname, user_id, encoding, loc);
pg_exec_query_dest(buf, dest, false);
if (mkdir(lp, S_IRWXU) != 0) {
UserAbortTransactionBlock();
elog(ERROR, "Unable to create database directory '%s'.", lp);
}
snprintf(buf, sizeof(buf), "%s %s%cbase%ctemplate1%c* '%s'",
COPY_CMD, DataDir, SEP_CHAR, SEP_CHAR, SEP_CHAR, lp);
system(buf);
if (system(buf) != 0) {
rmdir(lp);
UserAbortTransactionBlock();
elog(ERROR, "Could not initialize database directory.");
}
snprintf(buf, sizeof(buf),
"insert into pg_database (datname, datdba, encoding, datpath)"
" values ('%s', '%d', '%d', '%s');", dbname, user_id, encoding,
loc);
pg_exec_query_dest(buf, dest, false);
if (IsTransactionBlock())
EndTransactionBlock();
}
void
dropdb(char *dbname, CommandDest dest)
{
@ -97,6 +123,10 @@ dropdb(char *dbname, CommandDest dest)
ScanKeyData key;
HeapTuple tup;
/* no single quotes in dbname */
if (strchr(dbname, '\'') != NULL)
elog(ERROR, "Single quotes are not allowed in database names.");
/*
* If this call returns, the database exists and we're allowed to
* remove it.
@ -109,13 +139,19 @@ dropdb(char *dbname, CommandDest dest)
path = ExpandDatabasePath(dbpath);
if (path == NULL)
elog(ERROR, "Unable to locate path '%s'"
"\n\tThis may be due to a missing environment variable"
" in the server", dbpath);
elog(ERROR, "The path '%s' is invalid.\n"
"This may be due to a missing environment variable"
" on the server.", path);
/* stop the vacuum daemon (dead code...) */
stop_vacuum(dbpath, dbname);
/* don't call this in a transaction block */
if (IsTransactionBlock())
elog(ERROR, "dropdb: May not be called in a transaction block.");
else
BeginTransactionBlock();
/*
* Obtain exclusive lock on pg_database. We need this to ensure
* that no new backend starts up in the target database while we
@ -130,9 +166,12 @@ dropdb(char *dbname, CommandDest dest)
/*
* Check for active backends in the target database.
*/
if (DatabaseHasActiveBackends(db_id))
elog(ERROR, "Database '%s' has running backends, can't destroy it",
if (DatabaseHasActiveBackends(db_id)) {
heap_close(pgdbrel, AccessExclusiveLock);
UserAbortTransactionBlock();
elog(ERROR, "Database '%s' has running backends, can't drop it.",
dbname);
}
/*
* Find the database's tuple by OID (should be unique, we trust).
@ -146,6 +185,7 @@ dropdb(char *dbname, CommandDest dest)
if (!HeapTupleIsValid(tup))
{
heap_close(pgdbrel, AccessExclusiveLock);
UserAbortTransactionBlock();
elog(ERROR, "Database '%s', OID %u, not found in pg_database",
dbname, db_id);
}
@ -179,10 +219,16 @@ dropdb(char *dbname, CommandDest dest)
/*
* Remove the database's subdirectory and everything in it.
*/
snprintf(buf, sizeof(buf), "rm -r '%s'", path);
system(buf);
snprintf(buf, sizeof(buf), "rm -rf '%s'", path);
if (system(buf)!=0)
elog(NOTICE, "The database directory '%s' could not be removed.", path);
if (IsTransactionBlock())
EndTransactionBlock();
}
static HeapTuple
get_pg_dbtup(char *command, char *dbname, Relation dbrel)
{
@ -252,7 +298,7 @@ check_permissions(char *command,
/* Check to make sure user has permission to use createdb */
if (!use_createdb)
{
elog(ERROR, "user '%s' is not allowed to create/destroy databases",
elog(ERROR, "user '%s' is not allowed to create/drop databases",
userName);
}