diff --git a/doc/src/sgml/ref/alter_user.sgml b/doc/src/sgml/ref/alter_user.sgml
index 686684632a5..75bcca86546 100644
--- a/doc/src/sgml/ref/alter_user.sgml
+++ b/doc/src/sgml/ref/alter_user.sgml
@@ -1,5 +1,5 @@
@@ -23,7 +23,10 @@ Postgres documentation
1999-07-20
-ALTER USER username [ WITH PASSWORD password ]
+ALTER USER username
+ [ WITH
+ [ SYSID uid ]
+ [ PASSWORD password ] ]
[ CREATEDB | NOCREATEDB ] [ CREATEUSER | NOCREATEUSER ]
[ IN GROUP groupname [, ...] ]
[ VALID UNTIL 'abstime' ]
@@ -62,6 +65,22 @@ ALTER USER username [ WITH PASSWORD
+
+ uid
+
+
+ The new PostgreSQL user id of the user.
+ Since this number is used as a key into the
+ pg_shadow/pg_user table
+ throughout the system catalogs, it is not recommended that you change
+ it unless the user in question does not own anything at all and/or
+ you really know what you are doing. Note that it is not necessary that
+ database and UNIX user ids match, but some people
+ choose to keep the numbers the same.
+
+
+
+
groupname
@@ -130,9 +149,7 @@ ERROR: alterUser: user "username" does not exist
ALTER USER is used to change the attributes of a user's
Postgres account.
- Please note that it is not possible
- to alter a user's "usesysid" via the alter user
- statement. Also, it is only possible for the
+ Also, it is only possible for the
Postgres
user or any user with read and modify permissions on
pg_shadow to alter user passwords.
@@ -161,9 +178,7 @@ ERROR: alterUser: user "username" does not exist
to create or remove a user account.
- In the current release (v6.5), the IN GROUP clause is parsed
- but has no affect. When it is fully implemented, it is
- intended to modify the pg_group relation.
+ The IN GROUP clause is not yet implemented.
diff --git a/doc/src/sgml/ref/create_user.sgml b/doc/src/sgml/ref/create_user.sgml
index b7a55d05d48..9eccd3f7130 100644
--- a/doc/src/sgml/ref/create_user.sgml
+++ b/doc/src/sgml/ref/create_user.sgml
@@ -1,5 +1,5 @@
@@ -23,8 +23,10 @@ Postgres documentation
1999-07-20
-CREATE USER username
- [ WITH PASSWORD password ]
+CREATE USER username
+ [ WITH
+ [ SYSID uid ]
+ [ PASSWORD password ] ]
[ CREATEDB | NOCREATEDB ] [ CREATEUSER | NOCREATEUSER ]
[ IN GROUP groupname [, ...] ]
[ VALID UNTIL 'abstime' ]
@@ -49,11 +51,28 @@ CREATE USER username
+
+ uid
+
+
+ The SYSID clause can be used to choose
+ the PostgreSQL user id of the user
+ that is being created. It is not at all necessary that those
+ match the UNIX user ids, but some people
+ choose to keep the numbers the same.
+
+
+ If this is not specified, the highest assigned user id plus one
+ will be used as default.
+
+
+
+
password
- The WITH PASSWORD clause sets the user's password within
+ The PASSWORD clause sets the user's password within
the "pg_shadow" table. For this reason,
"pg_shadow" is no
longer accessible to the instance of
@@ -173,30 +192,9 @@ CREATE USER
CREATE USER will add a new user to an instance of
- Postgres.
+ PostgreSQL.
-
- The new user will be given a usesysid of:
-
-
-SELECT MAX(usesysid) + 1 FROM pg_shadow;
-
-
- This means that
- Postgres users' usesysids will not
- correspond to their operating
- system(OS) user ids. The exception to this rule is
- the postgres superuser, whose OS user id
- is used as the
- usesysid during the initdb process.
- If you still want the
- OS user id and the usesysid to match
- for any given user,
- use the createuser script provided with
- the Postgres distribution.
-
-
1998-09-21
@@ -216,19 +214,17 @@ SELECT MAX(usesysid) + 1 FROM pg_shadow;
Refer to the pg_shadow table for further information.
- Table = pg_shadow
- +--------------------------+--------------------------+-------+
- | Field | Type | Length|
- +--------------------------+--------------------------+-------+
- | usename | name | 32 |
- | usesysid | int4 | 4 |
- | usecreatedb | bool | 1 |
- | usetrace | bool | 1 |
- | usesuper | bool | 1 |
- | usecatupd | bool | 1 |
- | passwd | text | var |
- | valuntil | abstime | 4 |
- +--------------------------+--------------------------+-------+
+ Table "pg_shadow"
+ Attribute | Type | Extra
+-------------+---------+-------
+ usename | name |
+ usesysid | int4 |
+ usecreatedb | bool |
+ usetrace | bool |
+ usesuper | bool |
+ usecatupd | bool |
+ passwd | text |
+ valuntil | abstime |
diff --git a/src/backend/commands/user.c b/src/backend/commands/user.c
index 27a11cbcaf1..044779b7ee2 100644
--- a/src/backend/commands/user.c
+++ b/src/backend/commands/user.c
@@ -5,7 +5,7 @@
*
* Copyright (c) 1994, Regents of the University of California
*
- * $Id: user.c,v 1.38 1999/11/24 16:52:32 momjian Exp $
+ * $Id: user.c,v 1.39 1999/11/30 03:57:23 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@@ -105,14 +105,15 @@ DefineUser(CreateUserStmt *stmt, CommandDest dest)
TupleDesc pg_shadow_dsc;
HeapScanDesc scan;
HeapTuple tuple;
- Datum datum;
- bool exists = false,
- n,
+ bool user_exists = false,
+ sysid_exists = false,
inblock,
+ havesysid,
havepassword,
havevaluntil;
int max_id = -1;
+ havesysid = stmt->sysid >= 0;
havepassword = stmt->password && stmt->password[0];
havevaluntil = stmt->validUntil && stmt->validUntil[0];
@@ -129,13 +130,13 @@ DefineUser(CreateUserStmt *stmt, CommandDest dest)
if (pg_aclcheck(ShadowRelationName, pg_shadow, ACL_RD | ACL_WR | ACL_AP) != ACLCHECK_OK)
{
UserAbortTransactionBlock();
- elog(ERROR, "defineUser: user \"%s\" does not have SELECT and INSERT privilege for \"%s\"",
+ elog(ERROR, "DefineUser: user \"%s\" does not have SELECT and INSERT privilege for \"%s\"",
pg_shadow, ShadowRelationName);
return;
}
/*
- * Scan the pg_shadow relation to be certain the user doesn't already
+ * Scan the pg_shadow relation to be certain the user or id doesn't already
* exist. Note we secure exclusive lock, because we also need to be
* sure of what the next usesysid should be, and we need to protect
* our update of the flat password file.
@@ -144,25 +145,33 @@ DefineUser(CreateUserStmt *stmt, CommandDest dest)
pg_shadow_dsc = RelationGetDescr(pg_shadow_rel);
scan = heap_beginscan(pg_shadow_rel, false, SnapshotNow, 0, NULL);
- while (HeapTupleIsValid(tuple = heap_getnext(scan, 0)))
+ while (!user_exists && !sysid_exists && HeapTupleIsValid(tuple = heap_getnext(scan, 0)))
{
- datum = heap_getattr(tuple, Anum_pg_shadow_usename, pg_shadow_dsc, &n);
+ Datum datum;
+ bool null;
- if (!exists && !strncmp((char *) datum, stmt->user, strlen(stmt->user)))
- exists = true;
+ datum = heap_getattr(tuple, Anum_pg_shadow_usename, pg_shadow_dsc, &null);
+ user_exists = datum && !null && (strcmp((char *) datum, stmt->user) == 0);
- datum = heap_getattr(tuple, Anum_pg_shadow_usesysid, pg_shadow_dsc, &n);
- if ((int) datum > max_id)
- max_id = (int) datum;
+ datum = heap_getattr(tuple, Anum_pg_shadow_usesysid, pg_shadow_dsc, &null);
+ if (havesysid) /* customized id wanted */
+ sysid_exists = datum && !null && ((int)datum == stmt->sysid);
+ else /* pick 1 + max */
+ {
+ if ((int) datum > max_id)
+ max_id = (int) datum;
+ }
}
heap_endscan(scan);
- if (exists)
+ if (user_exists || sysid_exists)
{
heap_close(pg_shadow_rel, AccessExclusiveLock);
UserAbortTransactionBlock();
- elog(ERROR,
- "defineUser: user \"%s\" has already been created", stmt->user);
+ if (user_exists)
+ elog(ERROR, "DefineUser: user name \"%s\" already exists", stmt->user);
+ else
+ elog(ERROR, "DefineUser: sysid %d is already assigned", stmt->sysid);
return;
}
@@ -184,7 +193,7 @@ DefineUser(CreateUserStmt *stmt, CommandDest dest)
"values('%s',%d,'%c','f','%c','%c',%s%s%s,%s%s%s)",
ShadowRelationName,
stmt->user,
- max_id + 1,
+ havesysid ? stmt->sysid : max_id + 1,
(stmt->createdb && *stmt->createdb) ? 't' : 'f',
(stmt->createuser && *stmt->createuser) ? 't' : 'f',
((stmt->createdb && *stmt->createdb) ||
@@ -234,6 +243,7 @@ AlterUser(AlterUserStmt *stmt, CommandDest dest)
TupleDesc pg_shadow_dsc;
HeapTuple tuple;
bool inblock;
+ bool comma = false;
if (stmt->password)
CheckPgUserAclNotNull();
@@ -248,7 +258,7 @@ AlterUser(AlterUserStmt *stmt, CommandDest dest)
if (pg_aclcheck(ShadowRelationName, pg_shadow, ACL_RD | ACL_WR) != ACLCHECK_OK)
{
UserAbortTransactionBlock();
- elog(ERROR, "alterUser: user \"%s\" does not have SELECT and UPDATE privilege for \"%s\"",
+ elog(ERROR, "AlterUser: user \"%s\" does not have SELECT and UPDATE privilege for \"%s\"",
pg_shadow, ShadowRelationName);
return;
}
@@ -268,43 +278,79 @@ AlterUser(AlterUserStmt *stmt, CommandDest dest)
{
heap_close(pg_shadow_rel, AccessExclusiveLock);
UserAbortTransactionBlock();
- elog(ERROR, "alterUser: user \"%s\" does not exist", stmt->user);
+ elog(ERROR, "AlterUser: user \"%s\" does not exist", stmt->user);
}
+ /* look for duplicate sysid */
+ tuple = SearchSysCacheTuple(USESYSID,
+ Int32GetDatum(stmt->sysid),
+ 0, 0, 0);
+ if (HeapTupleIsValid(tuple))
+ {
+ Datum datum;
+ bool null;
+
+ datum = heap_getattr(tuple, Anum_pg_shadow_usename, pg_shadow_dsc, &null);
+ if (datum && !null && strcmp((char *) datum, stmt->user) != 0)
+ {
+ heap_close(pg_shadow_rel, AccessExclusiveLock);
+ UserAbortTransactionBlock();
+ elog(ERROR, "AlterUser: sysid %d is already assigned", stmt->sysid);
+ }
+ }
+
+
/*
* Create the update statement to modify the user.
*
* XXX see diatribe in preceding routine. This code is just as bogus.
*/
- snprintf(sql, SQL_LENGTH, "update %s set", ShadowRelationName);
+ snprintf(sql, SQL_LENGTH, "update %s set ", ShadowRelationName);
if (stmt->password)
+ {
snprintf(sql + strlen(sql), SQL_LENGTH - strlen(sql),
- " passwd = '%s'", stmt->password);
+ "passwd = '%s'", stmt->password);
+ comma = true;
+ }
+
+ if (stmt->sysid>=0)
+ {
+ if (comma)
+ strcat(sql, ", ");
+ snprintf(sql + strlen(sql), SQL_LENGTH - strlen(sql),
+ "usesysid = %d", stmt->sysid);
+ comma = true;
+ }
if (stmt->createdb)
- {
+ {
+ if (comma)
+ strcat(sql, ", ");
snprintf(sql + strlen(sql), SQL_LENGTH - strlen(sql),
- "%s usecreatedb='%s'",
- stmt->password ? "," : "",
- *stmt->createdb ? "t" : "f");
- }
+ "usecreatedb='%c'",
+ *stmt->createdb ? 't' : 'f');
+ comma = true;
+ }
if (stmt->createuser)
- {
+ {
+ if (comma)
+ strcat(sql, ", ");
snprintf(sql + strlen(sql), SQL_LENGTH - strlen(sql),
- "%s usesuper='%s'",
- (stmt->password || stmt->createdb) ? "," : "",
- *stmt->createuser ? "t" : "f");
- }
+ "usesuper='%c'",
+ *stmt->createuser ? 't' : 'f');
+ comma = true;
+ }
if (stmt->validUntil)
- {
+ {
+ if (comma)
+ strcat(sql, ", ");
snprintf(sql + strlen(sql), SQL_LENGTH - strlen(sql),
- "%s valuntil='%s'",
- (stmt->password || stmt->createdb || stmt->createuser) ? "," : "",
+ "valuntil='%s'",
stmt->validUntil);
- }
+ }
snprintf(sql + strlen(sql), SQL_LENGTH - strlen(sql),
" where usename = '%s'",
@@ -362,7 +408,7 @@ RemoveUser(char *user, CommandDest dest)
if (pg_aclcheck(ShadowRelationName, pg_shadow, ACL_RD | ACL_WR) != ACLCHECK_OK)
{
UserAbortTransactionBlock();
- elog(ERROR, "removeUser: user \"%s\" does not have SELECT and DELETE privilege for \"%s\"",
+ elog(ERROR, "RemoveUser: user \"%s\" does not have SELECT and DELETE privilege for \"%s\"",
pg_shadow, ShadowRelationName);
}
@@ -381,7 +427,7 @@ RemoveUser(char *user, CommandDest dest)
{
heap_close(pg_shadow_rel, AccessExclusiveLock);
UserAbortTransactionBlock();
- elog(ERROR, "removeUser: user \"%s\" does not exist", user);
+ elog(ERROR, "RemoveUser: user \"%s\" does not exist", user);
}
usesysid = (int32) heap_getattr(tuple, Anum_pg_shadow_usesysid, pg_dsc, &n);
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 5a291dc867f..c3d72896f65 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -10,7 +10,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.115 1999/11/20 21:39:36 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.116 1999/11/30 03:57:24 momjian Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
@@ -137,6 +137,7 @@ static Node *doNegate(Node *n);
%type user_createdb_clause, user_createuser_clause
%type user_passwd_clause
+%type sysid_clause
%type user_valid_clause
%type user_group_list, user_group_clause
@@ -295,6 +296,7 @@ static Node *doNegate(Node *n);
VALUES, VARCHAR, VARYING, VIEW,
WHEN, WHERE, WITH, WORK, YEAR_P, ZONE
+
/* Keywords (in SQL3 reserved words) */
%token DEFERRABLE, DEFERRED,
IMMEDIATE, INITIALLY,
@@ -323,7 +325,7 @@ static Node *doNegate(Node *n);
NEW, NOCREATEDB, NOCREATEUSER, NONE, NOTHING, NOTIFY, NOTNULL,
OFFSET, OIDS, OPERATOR, PASSWORD, PROCEDURAL,
RENAME, RESET, RETURNS, ROW, RULE,
- SEQUENCE, SERIAL, SETOF, SHARE, SHOW, START, STATEMENT, STDIN, STDOUT,
+ SEQUENCE, SERIAL, SETOF, SHARE, SHOW, START, STATEMENT, STDIN, STDOUT, SYSID,
TRUNCATE, TRUSTED,
UNLISTEN, UNTIL, VACUUM, VALID, VERBOSE, VERSION
@@ -443,18 +445,34 @@ stmt : AddAttrStmt
*
*****************************************************************************/
-CreateUserStmt: CREATE USER UserId user_passwd_clause user_createdb_clause
- user_createuser_clause user_group_clause user_valid_clause
+CreateUserStmt: CREATE USER UserId
+ user_createdb_clause user_createuser_clause user_group_clause
+ user_valid_clause
{
CreateUserStmt *n = makeNode(CreateUserStmt);
n->user = $3;
- n->password = $4;
- n->createdb = $5;
- n->createuser = $6;
- n->groupElts = $7;
- n->validUntil = $8;
+ n->sysid = -1;
+ n->password = NULL;
+ n->createdb = $4;
+ n->createuser = $5;
+ n->groupElts = $6;
+ n->validUntil = $7;
$$ = (Node *)n;
}
+ | CREATE USER UserId WITH sysid_clause user_passwd_clause
+ user_createdb_clause user_createuser_clause user_group_clause
+ user_valid_clause
+ {
+ CreateUserStmt *n = makeNode(CreateUserStmt);
+ n->user = $3;
+ n->sysid = $5;
+ n->password = $6;
+ n->createdb = $7;
+ n->createuser = $8;
+ n->groupElts = $9;
+ n->validUntil = $10;
+ $$ = (Node *)n;
+ }
;
/*****************************************************************************
@@ -464,16 +482,31 @@ CreateUserStmt: CREATE USER UserId user_passwd_clause user_createdb_clause
*
*****************************************************************************/
-AlterUserStmt: ALTER USER UserId user_passwd_clause user_createdb_clause
- user_createuser_clause user_group_clause user_valid_clause
+AlterUserStmt: ALTER USER UserId user_createdb_clause
+ user_createuser_clause user_group_clause user_valid_clause
{
AlterUserStmt *n = makeNode(AlterUserStmt);
n->user = $3;
- n->password = $4;
- n->createdb = $5;
- n->createuser = $6;
- n->groupElts = $7;
- n->validUntil = $8;
+ n->sysid = -1;
+ n->password = NULL;
+ n->createdb = $4;
+ n->createuser = $5;
+ n->groupElts = $6;
+ n->validUntil = $7;
+ $$ = (Node *)n;
+ }
+ | ALTER USER UserId WITH sysid_clause user_passwd_clause
+ user_createdb_clause
+ user_createuser_clause user_group_clause user_valid_clause
+ {
+ AlterUserStmt *n = makeNode(AlterUserStmt);
+ n->user = $3;
+ n->sysid = $5;
+ n->password = $6;
+ n->createdb = $7;
+ n->createuser = $8;
+ n->groupElts = $9;
+ n->validUntil = $10;
$$ = (Node *)n;
}
;
@@ -493,10 +526,14 @@ DropUserStmt: DROP USER UserId
}
;
-user_passwd_clause: WITH PASSWORD UserId { $$ = $3; }
- | /*EMPTY*/ { $$ = NULL; }
+user_passwd_clause: PASSWORD UserId { $$ = $2; }
+ | /*EMPTY*/ { $$ = NULL; }
;
+sysid_clause: SYSID Iconst { $$ = $2; }
+ | /*EMPTY*/ { $$ = -1; }
+ ;
+
user_createdb_clause: CREATEDB
{
bool* b;
@@ -537,7 +574,13 @@ user_group_list: user_group_list ',' UserId
}
;
-user_group_clause: IN GROUP user_group_list { $$ = $3; }
+user_group_clause: IN GROUP user_group_list
+ {
+ /* the backend doesn't actually process this,
+ * so an error message is probably fairer */
+ yyerror("IN GROUP is not implemented");
+ /* $$ = $3; */
+ }
| /*EMPTY*/ { $$ = NULL; }
;
@@ -4764,6 +4807,7 @@ ColId: IDENT { $$ = $1; }
| STATEMENT { $$ = "statement"; }
| STDIN { $$ = "stdin"; }
| STDOUT { $$ = "stdout"; }
+ | SYSID { $$ = "sysid"; }
| TIME { $$ = "time"; }
| TIMESTAMP { $$ = "timestamp"; }
| TIMEZONE_HOUR { $$ = "timezone_hour"; }
diff --git a/src/backend/parser/keywords.c b/src/backend/parser/keywords.c
index 392e7509c8e..3cb4e2787d5 100644
--- a/src/backend/parser/keywords.c
+++ b/src/backend/parser/keywords.c
@@ -7,7 +7,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/parser/keywords.c,v 1.63 1999/10/15 01:49:41 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/parser/keywords.c,v 1.64 1999/11/30 03:57:26 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@@ -219,6 +219,7 @@ static ScanKeyword ScanKeywords[] = {
{"stdin", STDIN},
{"stdout", STDOUT},
{"substring", SUBSTRING},
+ {"sysid", SYSID},
{"table", TABLE},
{"temp", TEMP},
{"temporary", TEMPORARY},
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index aac3ef7de53..04c8c9b5182 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -6,7 +6,7 @@
*
* Copyright (c) 1994, Regents of the University of California
*
- * $Id: parsenodes.h,v 1.86 1999/10/26 03:12:39 momjian Exp $
+ * $Id: parsenodes.h,v 1.87 1999/11/30 03:57:29 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@@ -237,6 +237,7 @@ typedef struct CreateUserStmt
NodeTag type;
char *user; /* PostgreSQL user login */
char *password; /* PostgreSQL user password */
+ int sysid; /* PgSQL system id (-1 if don't care) */
bool *createdb; /* Can the user create databases? */
bool *createuser; /* Can this user create users? */
List *groupElts; /* The groups the user is a member of */