From a833c441fd3cb92bb9cb169a6e73df8f1c6ffe95 Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Sun, 24 Feb 2002 20:20:21 +0000 Subject: [PATCH] Add OWNER option to CREATE DATABASE, so superusers can create databases on behalf of unprivileged users. Also, make '=' optional in CREATE DATABASE syntax. From Gavin Sherry, with kibitzing and docs by Tom Lane. --- doc/src/sgml/ref/create_database.sgml | 32 ++++++++++++++++++++----- doc/src/sgml/release.sgml | 3 ++- src/backend/commands/dbcommands.c | 33 +++++++++++++++++++++----- src/backend/nodes/copyfuncs.c | 4 +++- src/backend/nodes/equalfuncs.c | 4 +++- src/backend/parser/gram.y | 34 ++++++++++++++++++++------- src/backend/tcop/utility.c | 7 +++--- src/include/commands/dbcommands.h | 7 +++--- src/include/nodes/parsenodes.h | 3 ++- 9 files changed, 97 insertions(+), 30 deletions(-) diff --git a/doc/src/sgml/ref/create_database.sgml b/doc/src/sgml/ref/create_database.sgml index b208f100ea..2c87d21591 100644 --- a/doc/src/sgml/ref/create_database.sgml +++ b/doc/src/sgml/ref/create_database.sgml @@ -1,5 +1,5 @@ @@ -24,9 +24,10 @@ PostgreSQL documentation CREATE DATABASE name - [ WITH [ LOCATION = 'dbpath' ] - [ TEMPLATE = template ] - [ ENCODING = encoding ] ] + [ WITH [ OWNER [ = ] dbowner ] + [ LOCATION [ = ] 'dbpath' ] + [ TEMPLATE [ = ] template ] + [ ENCODING [ = ] encoding ] ] @@ -47,6 +48,16 @@ CREATE DATABASE name + + dbowner + + + Name of the database user who will own the new database, + or DEFAULT to use the default (namely, the + user executing the command). + + + dbpath @@ -171,7 +182,15 @@ CREATE DATABASE name CREATE DATABASE creates a new PostgreSQL database. - The creator becomes the owner of the new database. + + + + Normally, the creator becomes the owner of the new database. + A different owner may be specified by using the @@ -327,7 +346,8 @@ Type: \copyright for distribution terms There is no CREATE DATABASE statement in SQL92. - Databases are equivalent to catalogs whose creation is implementation-defined. + Databases are equivalent to catalogs, whose creation is + implementation-defined. diff --git a/doc/src/sgml/release.sgml b/doc/src/sgml/release.sgml index fae659919d..edcdc9405a 100644 --- a/doc/src/sgml/release.sgml +++ b/doc/src/sgml/release.sgml @@ -1,5 +1,5 @@ @@ -26,6 +26,7 @@ worries about funny characters. diff --git a/src/backend/commands/dbcommands.c b/src/backend/commands/dbcommands.c index 142fefb60f..712df38ec5 100644 --- a/src/backend/commands/dbcommands.c +++ b/src/backend/commands/dbcommands.c @@ -9,7 +9,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/dbcommands.c,v 1.82 2002/02/23 20:55:46 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/dbcommands.c,v 1.83 2002/02/24 20:20:19 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -34,6 +34,7 @@ #include "storage/sinval.h" #include "utils/builtins.h" #include "utils/fmgroids.h" +#include "utils/lsyscache.h" #include "utils/syscache.h" #ifdef MULTIBYTE @@ -55,8 +56,9 @@ static bool remove_dbdirs(const char *real_loc, const char *altloc); */ void -createdb(const char *dbname, const char *dbpath, - const char *dbtemplate, int encoding) +createdb(const char *dbname, const char *dbowner, + const char *dbpath, const char *dbtemplate, + int encoding) { char *nominal_loc; char *alt_loc; @@ -79,12 +81,31 @@ createdb(const char *dbname, const char *dbpath, Datum new_record[Natts_pg_database]; char new_record_nulls[Natts_pg_database]; Oid dboid; + int32 datdba; + /* obtain sysid of proposed owner */ + if (dbowner) + datdba = get_usesysid(dbowner); /* will elog if no such user */ + else + datdba = GetUserId(); + + /* check permission to create database */ if (!get_user_info(GetUserId(), &use_super, &use_createdb)) elog(ERROR, "current user name is invalid"); - if (!use_createdb && !use_super) - elog(ERROR, "CREATE DATABASE: permission denied"); + if (datdba == (int32) GetUserId()) + { + /* creating database for self: can be superuser or createdb */ + if (!use_createdb && !use_super) + elog(ERROR, "CREATE DATABASE: permission denied"); + } + else + { + /* creating database for someone else: must be superuser */ + /* note that the someone else need not have any permissions */ + if (!use_super) + elog(ERROR, "CREATE DATABASE: permission denied"); + } /* don't call this in a transaction block */ if (IsTransactionBlock()) @@ -254,7 +275,7 @@ createdb(const char *dbname, const char *dbpath, /* Form tuple */ new_record[Anum_pg_database_datname - 1] = DirectFunctionCall1(namein, CStringGetDatum(dbname)); - new_record[Anum_pg_database_datdba - 1] = Int32GetDatum(GetUserId()); + new_record[Anum_pg_database_datdba - 1] = Int32GetDatum(datdba); new_record[Anum_pg_database_encoding - 1] = Int32GetDatum(encoding); new_record[Anum_pg_database_datistemplate - 1] = BoolGetDatum(false); new_record[Anum_pg_database_datallowconn - 1] = BoolGetDatum(true); diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index 153d257d26..03ceb08eba 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -15,7 +15,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.161 2002/02/18 23:11:14 petere Exp $ + * $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.162 2002/02/24 20:20:20 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -2231,6 +2231,8 @@ _copyCreatedbStmt(CreatedbStmt *from) if (from->dbname) newnode->dbname = pstrdup(from->dbname); + if (from->dbowner) + newnode->dbowner = pstrdup(from->dbowner); if (from->dbpath) newnode->dbpath = pstrdup(from->dbpath); if (from->dbtemplate) diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c index 886963f880..2ea41d6e52 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -20,7 +20,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.109 2002/02/18 23:11:14 petere Exp $ + * $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.110 2002/02/24 20:20:20 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1099,6 +1099,8 @@ _equalCreatedbStmt(CreatedbStmt *a, CreatedbStmt *b) { if (!equalstr(a->dbname, b->dbname)) return false; + if (!equalstr(a->dbowner, b->dbowner)) + return false; if (!equalstr(a->dbpath, b->dbpath)) return false; if (!equalstr(a->dbtemplate, b->dbtemplate)) diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index a23273c1a7..37d9364eae 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -11,7 +11,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.278 2002/02/18 23:11:17 petere Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.279 2002/02/24 20:20:20 tgl Exp $ * * HISTORY * AUTHOR DATE MAJOR EVENT @@ -153,6 +153,7 @@ static void doNegateFloat(Value *v); %type drop_behavior %type createdb_opt_list, createdb_opt_item +%type opt_equal %type opt_lock, lock_type %type opt_force, opt_or_replace @@ -733,6 +734,7 @@ CreateSchemaStmt: CREATE SCHEMA UserId /* for now, just make this the same as CREATE DATABASE */ CreatedbStmt *n = makeNode(CreatedbStmt); n->dbname = $3; + n->dbowner = NULL; n->dbpath = NULL; n->dbtemplate = NULL; n->encoding = -1; @@ -3049,6 +3051,7 @@ CreatedbStmt: CREATE DATABASE database_name WITH createdb_opt_list n->dbname = $3; /* set default options */ + n->dbowner = NULL; n->dbpath = NULL; n->dbtemplate = NULL; n->encoding = -1; @@ -3068,6 +3071,9 @@ CreatedbStmt: CREATE DATABASE database_name WITH createdb_opt_list case 3: n->encoding = lfirsti(lnext(optitem)); break; + case 4: + n->dbowner = (char *) lsecond(optitem); + break; } } $$ = (Node *)n; @@ -3076,6 +3082,7 @@ CreatedbStmt: CREATE DATABASE database_name WITH createdb_opt_list { CreatedbStmt *n = makeNode(CreatedbStmt); n->dbname = $3; + n->dbowner = NULL; n->dbpath = NULL; n->dbtemplate = NULL; n->encoding = -1; @@ -3093,23 +3100,23 @@ createdb_opt_list: createdb_opt_item * createdb_opt_item returns 2-element lists, with the first element * being an integer code to indicate which item was specified. */ -createdb_opt_item: LOCATION '=' Sconst +createdb_opt_item: LOCATION opt_equal Sconst { $$ = lconsi(1, makeList1($3)); } - | LOCATION '=' DEFAULT + | LOCATION opt_equal DEFAULT { $$ = lconsi(1, makeList1(NULL)); } - | TEMPLATE '=' name + | TEMPLATE opt_equal name { $$ = lconsi(2, makeList1($3)); } - | TEMPLATE '=' DEFAULT + | TEMPLATE opt_equal DEFAULT { $$ = lconsi(2, makeList1(NULL)); } - | ENCODING '=' Sconst + | ENCODING opt_equal Sconst { int encoding; #ifdef MULTIBYTE @@ -3123,7 +3130,7 @@ createdb_opt_item: LOCATION '=' Sconst #endif $$ = lconsi(3, makeListi1(encoding)); } - | ENCODING '=' Iconst + | ENCODING opt_equal Iconst { #ifdef MULTIBYTE if (!pg_get_enconv_by_encoding($3)) @@ -3134,12 +3141,23 @@ createdb_opt_item: LOCATION '=' Sconst #endif $$ = lconsi(3, makeListi1($3)); } - | ENCODING '=' DEFAULT + | ENCODING opt_equal DEFAULT { $$ = lconsi(3, makeListi1(-1)); } + | OWNER opt_equal name + { + $$ = lconsi(4, makeList1($3)); + } + | OWNER opt_equal DEFAULT + { + $$ = lconsi(4, makeList1(NULL)); + } ; +opt_equal: '=' { $$ = TRUE; } + | /*EMPTY*/ { $$ = FALSE; } + ; /***************************************************************************** * diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c index 766512ae59..f80a6bdca4 100644 --- a/src/backend/tcop/utility.c +++ b/src/backend/tcop/utility.c @@ -10,7 +10,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/tcop/utility.c,v 1.125 2002/02/07 00:27:30 inoue Exp $ + * $Header: /cvsroot/pgsql/src/backend/tcop/utility.c,v 1.126 2002/02/24 20:20:20 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -624,8 +624,9 @@ ProcessUtility(Node *parsetree, set_ps_display(commandTag = "CREATE DATABASE"); - createdb(stmt->dbname, stmt->dbpath, - stmt->dbtemplate, stmt->encoding); + createdb(stmt->dbname, stmt->dbowner, + stmt->dbpath, stmt->dbtemplate, + stmt->encoding); } break; diff --git a/src/include/commands/dbcommands.h b/src/include/commands/dbcommands.h index 23c68d3c35..0636130c2e 100644 --- a/src/include/commands/dbcommands.h +++ b/src/include/commands/dbcommands.h @@ -7,15 +7,16 @@ * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: dbcommands.h,v 1.19 2001/11/05 17:46:33 momjian Exp $ + * $Id: dbcommands.h,v 1.20 2002/02/24 20:20:21 tgl Exp $ * *------------------------------------------------------------------------- */ #ifndef DBCOMMANDS_H #define DBCOMMANDS_H -extern void createdb(const char *dbname, const char *dbpath, - const char *dbtemplate, int encoding); +extern void createdb(const char *dbname, const char *dbowner, + const char *dbpath, const char *dbtemplate, + int encoding); extern void dropdb(const char *dbname); #endif /* DBCOMMANDS_H */ diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 252c509cc4..0b40fe9948 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: parsenodes.h,v 1.152 2002/02/18 23:11:41 petere Exp $ + * $Id: parsenodes.h,v 1.153 2002/02/24 20:20:21 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -679,6 +679,7 @@ typedef struct CreatedbStmt { NodeTag type; char *dbname; /* name of database to create */ + char *dbowner; /* name of owner (NULL = default) */ char *dbpath; /* location of database (NULL = default) */ char *dbtemplate; /* template to use (NULL = default) */ int encoding; /* MULTIBYTE encoding (-1 = use default) */ -- 2.40.0