postgresql/src/backend/commands/user.c

1079 lines
32 KiB
C
Raw Normal View History

/*-------------------------------------------------------------------------
*
* user.c
* use pg_exec_query to create a new user in the catalog
*
* Copyright (c) 1994, Regents of the University of California
*
* $Id: user.c,v 1.47 1999/12/21 22:39:01 wieck Exp $
*
*-------------------------------------------------------------------------
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include "postgres.h"
1999-07-16 07:00:38 +02:00
#include "access/heapam.h"
#include "catalog/catname.h"
#include "catalog/pg_database.h"
#include "catalog/pg_shadow.h"
#include "catalog/pg_group.h"
#include "catalog/indexing.h"
#include "commands/copy.h"
1999-07-16 07:00:38 +02:00
#include "commands/user.h"
#include "commands/trigger.h"
#include "libpq/crypt.h"
1999-07-16 07:00:38 +02:00
#include "miscadmin.h"
#include "nodes/pg_list.h"
#include "tcop/tcopprot.h"
#include "utils/acl.h"
#include "utils/array.h"
#include "utils/syscache.h"
static void CheckPgUserAclNotNull(void);
1999-05-25 18:15:34 +02:00
#define SQL_LENGTH 512
1998-12-14 07:50:32 +01:00
/*---------------------------------------------------------------------
1999-12-20 02:23:04 +01:00
* update_pg_pwd
*
* copy the modified contents of pg_shadow to a file used by the postmaster
* for user authentication. The file is stored as $PGDATA/pg_pwd.
*
* NB: caller is responsible for ensuring that only one backend can
* execute this routine at a time. Acquiring AccessExclusiveLock on
* pg_shadow is the standard way to do that.
*---------------------------------------------------------------------
*/
HeapTuple
1999-12-20 02:23:04 +01:00
update_pg_pwd(void)
{
1999-05-25 18:15:34 +02:00
char *filename,
*tempname;
int bufsize;
/*
* This is a trigger, so clean out the information provided by
* the trigger manager.
*/
CurrentTriggerData = NULL;
/*
* Create a temporary filename to be renamed later. This prevents the
* backend from clobbering the pg_pwd file while the postmaster might
* be reading from it.
*/
filename = crypt_getpwdfilename();
1998-12-14 07:50:32 +01:00
bufsize = strlen(filename) + 12;
tempname = (char *) palloc(bufsize);
snprintf(tempname, bufsize, "%s.%d", filename, MyProcPid);
/*
* Copy the contents of pg_shadow to the pg_pwd ASCII file using the
* SEPCHAR character as the delimiter between fields. Make sure the
* file is created with mode 600 (umask 077).
*/
DoCopy(ShadowRelationName, /* relname */
false, /* binary */
false, /* oids */
false, /* from */
false, /* pipe */
tempname, /* filename */
CRYPT_PWD_FILE_SEPSTR, /* delim */
"", /* nulls */
0077); /* fileumask */
/*
* And rename the temp file to its final name, deleting the old pg_pwd.
*/
rename(tempname, filename);
/*
* Create a flag file the postmaster will detect the next time it
* tries to authenticate a user. The postmaster will know to reload
* the pg_pwd file contents.
*/
filename = crypt_getpwdreloadfilename();
creat(filename, S_IRUSR | S_IWUSR);
pfree((void *) tempname);
return NULL;
}
/*---------------------------------------------------------------------
* DefineUser
*
* Add the user to the pg_shadow relation, and if specified make sure the
* user is specified in the desired groups of defined in pg_group.
*---------------------------------------------------------------------
*/
void
DefineUser(CreateUserStmt *stmt, CommandDest dest)
{
1999-05-25 18:15:34 +02:00
char *pg_shadow,
sql[SQL_LENGTH];
Relation pg_shadow_rel;
TupleDesc pg_shadow_dsc;
HeapScanDesc scan;
HeapTuple tuple;
bool user_exists = false,
sysid_exists = false,
1999-05-25 18:15:34 +02:00
inblock,
havesysid,
1999-05-25 18:15:34 +02:00
havepassword,
havevaluntil;
int max_id = -1;
List *item;
havesysid = stmt->sysid >= 0;
havepassword = stmt->password && stmt->password[0];
havevaluntil = stmt->validUntil && stmt->validUntil[0];
if (havepassword)
CheckPgUserAclNotNull();
if (!(inblock = IsTransactionBlock()))
BeginTransactionBlock();
/*
* Make sure the user attempting to create a user can insert into the
* pg_shadow relation.
*/
1998-03-06 19:03:38 +01:00
pg_shadow = GetPgUserName();
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\"",
1998-03-06 19:03:38 +01:00
pg_shadow, ShadowRelationName);
return;
}
/*
* 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.
*/
pg_shadow_rel = heap_openr(ShadowRelationName, AccessExclusiveLock);
1998-09-01 05:29:17 +02:00
pg_shadow_dsc = RelationGetDescr(pg_shadow_rel);
1998-07-27 21:38:40 +02:00
scan = heap_beginscan(pg_shadow_rel, false, SnapshotNow, 0, NULL);
while (!user_exists && !sysid_exists && HeapTupleIsValid(tuple = heap_getnext(scan, 0)))
{
Datum datum;
bool null;
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, &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 (user_exists || sysid_exists)
{
heap_close(pg_shadow_rel, AccessExclusiveLock);
UserAbortTransactionBlock();
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;
}
/*
* Build the insert statement to be executed.
*
1999-05-25 18:15:34 +02:00
* XXX Ugly as this code is, it still fails to cope with ' or \ in any of
* the provided strings.
*
* XXX This routine would be *lots* better if it inserted the new
* tuple with formtuple/heap_insert. For one thing, all of the
* transaction-block gamesmanship could be eliminated, because
* it's only there to make the world safe for a recursive call
* to pg_exec_query_dest().
*/
1999-05-25 18:15:34 +02:00
snprintf(sql, SQL_LENGTH,
"insert into %s (usename,usesysid,usecreatedb,usetrace,"
"usesuper,usecatupd,passwd,valuntil) "
"values('%s',%d,'%c','f','%c','%c',%s%s%s,%s%s%s)",
ShadowRelationName,
stmt->user,
havesysid ? stmt->sysid : max_id + 1,
(stmt->createdb && *stmt->createdb) ? 't' : 'f',
(stmt->createuser && *stmt->createuser) ? 't' : 'f',
((stmt->createdb && *stmt->createdb) ||
(stmt->createuser && *stmt->createuser)) ? 't' : 'f',
havepassword ? "'" : "",
havepassword ? stmt->password : "NULL",
havepassword ? "'" : "",
havevaluntil ? "'" : "",
havevaluntil ? stmt->validUntil : "NULL",
havevaluntil ? "'" : "");
/*
* XXX If insert fails, say because a bogus valuntil date is given,
* need to catch the resulting error and undo our transaction.
*/
pg_exec_query_dest(sql, dest, false);
/*
* Add the user to the groups specified. We'll just call the below
* AlterGroup for this.
*/
foreach(item, stmt->groupElts)
{
AlterGroupStmt ags;
ags.name = strVal(lfirst(item));
ags.action = +1;
ags.listUsers = lcons((void*)makeString(stmt->user), NIL);
AlterGroup(&ags, dest);
}
/*
* Write the updated pg_shadow data to the flat password file.
* Because we are still holding AccessExclusiveLock on pg_shadow,
* we can be sure no other backend will try to write the flat
* file at the same time.
*/
1999-12-20 02:23:04 +01:00
update_pg_pwd();
/*
* Now we can clean up.
*/
heap_close(pg_shadow_rel, AccessExclusiveLock);
if (IsTransactionBlock() && !inblock)
EndTransactionBlock();
}
extern void
AlterUser(AlterUserStmt *stmt, CommandDest dest)
{
1999-05-25 18:15:34 +02:00
char *pg_shadow,
sql[SQL_LENGTH];
Relation pg_shadow_rel;
TupleDesc pg_shadow_dsc;
HeapTuple tuple;
1999-05-25 18:15:34 +02:00
bool inblock;
bool comma = false;
if (stmt->password)
CheckPgUserAclNotNull();
if (!(inblock = IsTransactionBlock()))
BeginTransactionBlock();
/*
* Make sure the user attempting to create a user can insert into the
* pg_shadow relation.
*/
1998-03-06 19:03:38 +01:00
pg_shadow = GetPgUserName();
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\"",
1998-03-06 19:03:38 +01:00
pg_shadow, ShadowRelationName);
return;
}
/*
* Scan the pg_shadow relation to be certain the user exists.
* Note we secure exclusive lock to protect our update of the
* flat password file.
*/
pg_shadow_rel = heap_openr(ShadowRelationName, AccessExclusiveLock);
1998-09-01 05:29:17 +02:00
pg_shadow_dsc = RelationGetDescr(pg_shadow_rel);
tuple = SearchSysCacheTuple(SHADOWNAME,
PointerGetDatum(stmt->user),
0, 0, 0);
if (!HeapTupleIsValid(tuple))
{
heap_close(pg_shadow_rel, AccessExclusiveLock);
UserAbortTransactionBlock();
elog(ERROR, "AlterUser: user \"%s\" does not exist", stmt->user);
}
/* look for duplicate sysid */
1999-11-30 05:29:57 +01:00
tuple = SearchSysCacheTuple(SHADOWSYSID,
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);
1998-12-14 07:50:32 +01:00
if (stmt->password)
{
snprintf(sql + strlen(sql), SQL_LENGTH - strlen(sql),
"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;
}
1998-12-14 07:50:32 +01:00
if (stmt->createdb)
{
if (comma)
strcat(sql, ", ");
snprintf(sql + strlen(sql), SQL_LENGTH - strlen(sql),
"usecreatedb='%c'",
*stmt->createdb ? 't' : 'f');
comma = true;
}
1998-12-14 07:50:32 +01:00
if (stmt->createuser)
{
if (comma)
strcat(sql, ", ");
snprintf(sql + strlen(sql), SQL_LENGTH - strlen(sql),
"usesuper='%c'",
*stmt->createuser ? 't' : 'f');
comma = true;
}
1998-12-14 07:50:32 +01:00
if (stmt->validUntil)
{
if (comma)
strcat(sql, ", ");
snprintf(sql + strlen(sql), SQL_LENGTH - strlen(sql),
"valuntil='%s'",
1999-05-25 18:15:34 +02:00
stmt->validUntil);
}
snprintf(sql + strlen(sql), SQL_LENGTH - strlen(sql),
" where usename = '%s'",
stmt->user);
1998-12-14 07:50:32 +01:00
pg_exec_query_dest(sql, dest, false);
1998-12-14 07:50:32 +01:00
/*
* Add stuff here for groups?
*/
if (stmt->groupElts)
elog(NOTICE, "IN GROUP is not implemented for ALTER USER.");
/*
* Write the updated pg_shadow data to the flat password file.
* Because we are still holding AccessExclusiveLock on pg_shadow,
* we can be sure no other backend will try to write the flat
* file at the same time.
*/
1999-12-20 02:23:04 +01:00
update_pg_pwd();
/*
* Now we can clean up.
*/
heap_close(pg_shadow_rel, AccessExclusiveLock);
if (IsTransactionBlock() && !inblock)
EndTransactionBlock();
}
extern void
RemoveUser(char *user, CommandDest dest)
{
1998-03-06 19:03:38 +01:00
char *pg_shadow;
Relation pg_shadow_rel,
pg_rel;
TupleDesc pg_dsc;
HeapScanDesc scan;
HeapTuple tuple;
Datum datum;
char sql[SQL_LENGTH];
bool n,
inblock;
int32 usesysid;
int ndbase = 0;
char **dbase = NULL;
if (!(inblock = IsTransactionBlock()))
BeginTransactionBlock();
/*
* Make sure the user attempting to create a user can delete from the
* pg_shadow relation.
*/
1998-03-06 19:03:38 +01:00
pg_shadow = GetPgUserName();
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\"",
1998-03-06 19:03:38 +01:00
pg_shadow, ShadowRelationName);
}
/*
* Scan the pg_shadow relation to find the usesysid of the user to be
* deleted. Note we secure exclusive lock, because we need to protect
* our update of the flat password file.
*/
pg_shadow_rel = heap_openr(ShadowRelationName, AccessExclusiveLock);
1998-09-01 05:29:17 +02:00
pg_dsc = RelationGetDescr(pg_shadow_rel);
tuple = SearchSysCacheTuple(SHADOWNAME,
PointerGetDatum(user),
0, 0, 0);
if (!HeapTupleIsValid(tuple))
{
heap_close(pg_shadow_rel, AccessExclusiveLock);
UserAbortTransactionBlock();
elog(ERROR, "RemoveUser: user \"%s\" does not exist", user);
}
usesysid = (int32) heap_getattr(tuple, Anum_pg_shadow_usesysid, pg_dsc, &n);
/*
* Perform a scan of the pg_database relation to find the databases
* owned by usesysid. Then drop them.
*/
pg_rel = heap_openr(DatabaseRelationName, AccessExclusiveLock);
1998-09-01 05:29:17 +02:00
pg_dsc = RelationGetDescr(pg_rel);
1998-07-27 21:38:40 +02:00
scan = heap_beginscan(pg_rel, false, SnapshotNow, 0, NULL);
while (HeapTupleIsValid(tuple = heap_getnext(scan, 0)))
{
datum = heap_getattr(tuple, Anum_pg_database_datdba, pg_dsc, &n);
if ((int) datum == usesysid)
{
datum = heap_getattr(tuple, Anum_pg_database_datname, pg_dsc, &n);
if (memcmp((void *) datum, "template1", 9) != 0)
{
1999-05-25 18:15:34 +02:00
dbase =
(char **) repalloc((void *) dbase, sizeof(char *) * (ndbase + 1));
1998-12-14 07:50:32 +01:00
dbase[ndbase] = (char *) palloc(NAMEDATALEN + 1);
memcpy((void *) dbase[ndbase], (void *) datum, NAMEDATALEN);
dbase[ndbase++][NAMEDATALEN] = '\0';
}
}
}
heap_endscan(scan);
heap_close(pg_rel, AccessExclusiveLock);
while (ndbase--)
{
elog(NOTICE, "Dropping database %s", dbase[ndbase]);
snprintf(sql, SQL_LENGTH, "DROP DATABASE %s", dbase[ndbase]);
1998-12-14 07:50:32 +01:00
pfree((void *) dbase[ndbase]);
pg_exec_query_dest(sql, dest, false);
}
if (dbase)
1998-12-14 07:50:32 +01:00
pfree((void *) dbase);
/*
* Since pg_shadow is global over all databases, one of two things
* must be done to insure complete consistency. First, pg_shadow
* could be made non-global. This would elminate the code above for
* deleting database and would require the addition of code to delete
* tables, views, etc owned by the user.
*
* The second option would be to create a means of deleting tables, view,
* etc. owned by the user from other databases. pg_shadow is global
* and so this must be done at some point.
*
* Let us not forget that the user should be removed from the pg_groups
* also.
*
* Todd A. Brandys 11/18/1997
*
*/
/*
* Remove the user from the pg_shadow table
*/
1999-05-25 18:15:34 +02:00
snprintf(sql, SQL_LENGTH,
"delete from %s where usename = '%s'", ShadowRelationName, user);
pg_exec_query_dest(sql, dest, false);
/*
* Write the updated pg_shadow data to the flat password file.
* Because we are still holding AccessExclusiveLock on pg_shadow,
* we can be sure no other backend will try to write the flat
* file at the same time.
*/
1999-12-20 02:23:04 +01:00
update_pg_pwd();
/*
* Now we can clean up.
*/
heap_close(pg_shadow_rel, AccessExclusiveLock);
if (IsTransactionBlock() && !inblock)
EndTransactionBlock();
}
/*
* CheckPgUserAclNotNull
*
* check to see if there is an ACL on pg_shadow
*/
static void
CheckPgUserAclNotNull()
{
HeapTuple htup;
htup = SearchSysCacheTuple(RELNAME,
PointerGetDatum(ShadowRelationName),
0, 0, 0);
if (!HeapTupleIsValid(htup))
{
elog(ERROR, "IsPgUserAclNull: class \"%s\" not found",
ShadowRelationName);
}
if (heap_attisnull(htup, Anum_pg_class_relacl))
{
elog(NOTICE, "To use passwords, you have to revoke permissions on pg_shadow");
elog(NOTICE, "so normal users can not read the passwords.");
elog(ERROR, "Try 'REVOKE ALL ON pg_shadow FROM PUBLIC'");
}
return;
}
/*** GROUP THINGS ***/
void
CreateGroup(CreateGroupStmt *stmt, CommandDest dest)
{
Relation pg_group_rel;
HeapScanDesc scan;
HeapTuple tuple;
TupleDesc pg_group_dsc;
bool inblock;
bool group_exists = false,
sysid_exists = false;
int max_id = -1;
Datum new_record[Natts_pg_group];
char new_record_nulls[Natts_pg_group];
List *item, *newlist=NULL;
ArrayType *userarray;
if (!(inblock = IsTransactionBlock()))
BeginTransactionBlock();
/*
* Make sure the user can do this.
*/
if (pg_aclcheck(GroupRelationName, GetPgUserName(), ACL_RD | ACL_AP) != ACLCHECK_OK)
{
UserAbortTransactionBlock();
elog(ERROR, "CreateGroup: Permission denied.");
}
pg_group_rel = heap_openr(GroupRelationName, AccessExclusiveLock);
pg_group_dsc = RelationGetDescr(pg_group_rel);
scan = heap_beginscan(pg_group_rel, false, SnapshotNow, 0, NULL);
while (!group_exists && !sysid_exists && HeapTupleIsValid(tuple = heap_getnext(scan, false)))
{
Datum datum;
bool null;
datum = heap_getattr(tuple, Anum_pg_group_groname, pg_group_dsc, &null);
group_exists = datum && !null && (strcmp((char *) datum, stmt->name) == 0);
datum = heap_getattr(tuple, Anum_pg_group_grosysid, pg_group_dsc, &null);
if (stmt->sysid >= 0) /* 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 (group_exists || sysid_exists)
{
heap_close(pg_group_rel, AccessExclusiveLock);
UserAbortTransactionBlock();
if (group_exists)
elog(ERROR, "CreateGroup: Group name \"%s\" already exists.", stmt->name);
else
elog(ERROR, "CreateGroup: Group sysid %d is already assigned.", stmt->sysid);
}
/*
* Translate the given user names to ids
*/
foreach(item, stmt->initUsers)
{
const char * groupuser = strVal(lfirst(item));
Value *v;
tuple = SearchSysCacheTuple(SHADOWNAME,
PointerGetDatum(groupuser),
0, 0, 0);
if (!HeapTupleIsValid(tuple))
{
heap_close(pg_group_rel, AccessExclusiveLock);
UserAbortTransactionBlock();
elog(ERROR, "CreateGroup: User \"%s\" does not exist.", groupuser);
}
v = makeInteger(((Form_pg_shadow) GETSTRUCT(tuple))->usesysid);
if (!member(v, newlist))
newlist = lcons(v, newlist);
}
/* build an array to insert */
if (newlist)
{
int i;
userarray = palloc(ARR_OVERHEAD(1) + length(newlist) * sizeof(int32));
ARR_SIZE(userarray) = ARR_OVERHEAD(1) + length(newlist) * sizeof(int32);
ARR_FLAGS(userarray) = 0x0;
ARR_NDIM(userarray) = 1; /* one dimensional array */
ARR_LBOUND(userarray)[0] = 1; /* axis starts at one */
ARR_DIMS(userarray)[0] = length(newlist); /* axis is this long */
/* fill the array */
i = 0;
foreach(item, newlist)
{
((int*)ARR_DATA_PTR(userarray))[i++] = intVal(lfirst(item));
}
}
else
userarray = NULL;
/*
* Form a tuple to insert
*/
if (stmt->sysid >=0)
max_id = stmt->sysid;
else
max_id++;
new_record[Anum_pg_group_groname-1] = (Datum)(stmt->name);
new_record[Anum_pg_group_grosysid-1] = (Datum)(max_id);
new_record[Anum_pg_group_grolist-1] = (Datum)userarray;
new_record_nulls[Anum_pg_group_groname-1] = ' ';
new_record_nulls[Anum_pg_group_grosysid-1] = ' ';
new_record_nulls[Anum_pg_group_grolist-1] = userarray ? ' ' : 'n';
tuple = heap_formtuple(pg_group_dsc, new_record, new_record_nulls);
/*
* Insert a new record in the pg_group_table
*/
heap_insert(pg_group_rel, tuple);
/*
* Update indexes
*/
if (RelationGetForm(pg_group_rel)->relhasindex) {
Relation idescs[Num_pg_group_indices];
CatalogOpenIndices(Num_pg_group_indices,
Name_pg_group_indices, idescs);
CatalogIndexInsert(idescs, Num_pg_group_indices, pg_group_rel,
tuple);
CatalogCloseIndices(Num_pg_group_indices, idescs);
}
heap_close(pg_group_rel, NoLock);
if (IsTransactionBlock() && !inblock)
EndTransactionBlock();
}
void
AlterGroup(AlterGroupStmt *stmt, CommandDest dest)
{
Relation pg_group_rel;
TupleDesc pg_group_dsc;
bool inblock;
HeapTuple group_tuple;
if (!(inblock = IsTransactionBlock()))
BeginTransactionBlock();
/*
* Make sure the user can do this.
*/
if (pg_aclcheck(GroupRelationName, GetPgUserName(), ACL_RD | ACL_WR) != ACLCHECK_OK)
{
UserAbortTransactionBlock();
elog(ERROR, "AlterGroup: Permission denied.");
}
pg_group_rel = heap_openr(GroupRelationName, AccessExclusiveLock);
pg_group_dsc = RelationGetDescr(pg_group_rel);
/*
* Verify that group exists.
* If we find a tuple, will take that the rest of the way and make our
* modifications on it.
*/
if (!HeapTupleIsValid(group_tuple = SearchSysCacheTupleCopy(GRONAME, PointerGetDatum(stmt->name), 0, 0, 0)))
{
heap_close(pg_group_rel, AccessExclusiveLock);
UserAbortTransactionBlock();
elog(ERROR, "AlterGroup: Group \"%s\" does not exist.", stmt->name);
}
/*
* Now decide what to do.
*/
if (stmt->action == 0) /* change sysid */
{
ScanKeyData keys[2];
HeapTuple tuple;
HeapScanDesc scan;
Datum new_record[Natts_pg_group];
char new_record_nulls[Natts_pg_group];
bool null;
/*
* First check if the id is already assigned.
*/
ScanKeyEntryInitialize(&keys[0], 0x0, Anum_pg_group_grosysid, F_INT4EQ,
Int32GetDatum(stmt->sysid));
ScanKeyEntryInitialize(&keys[1], 0x0, Anum_pg_group_groname, F_NAMENE,
PointerGetDatum(stmt->name));
scan = heap_beginscan(pg_group_rel, false, SnapshotNow, 2, keys);
if (HeapTupleIsValid(heap_getnext(scan, false)))
{
heap_endscan(scan);
heap_close(pg_group_rel, AccessExclusiveLock);
UserAbortTransactionBlock();
elog(ERROR, "AlterGroup: Group sysid %d is already assigned.", stmt->sysid);
}
heap_endscan(scan);
/*
* Insert the new tuple with the updated sysid
*/
new_record[Anum_pg_group_groname-1] = (Datum)(stmt->name);
new_record[Anum_pg_group_grosysid-1] = (Datum)(stmt->sysid);
new_record[Anum_pg_group_grolist-1] = heap_getattr(group_tuple, Anum_pg_group_grolist, pg_group_dsc, &null);
new_record_nulls[Anum_pg_group_groname-1] = ' ';
new_record_nulls[Anum_pg_group_grosysid-1] = ' ';
new_record_nulls[Anum_pg_group_grolist-1] = null ? 'n' : ' ';
tuple = heap_formtuple(pg_group_dsc, new_record, new_record_nulls);
heap_update(pg_group_rel, &group_tuple->t_self, tuple, NULL);
/* Update indexes */
if (RelationGetForm(pg_group_rel)->relhasindex) {
Relation idescs[Num_pg_group_indices];
CatalogOpenIndices(Num_pg_group_indices,
Name_pg_group_indices, idescs);
CatalogIndexInsert(idescs, Num_pg_group_indices, pg_group_rel,
tuple);
CatalogCloseIndices(Num_pg_group_indices, idescs);
}
}
/*
* add users to group
*/
else if (stmt->action > 0)
{
Datum new_record[Natts_pg_group];
char new_record_nulls[Natts_pg_group] = { ' ', ' ', ' '};
ArrayType *newarray, *oldarray;
List * newlist = NULL, *item;
HeapTuple tuple;
bool null = false;
Datum datum = heap_getattr(group_tuple, Anum_pg_group_grolist, pg_group_dsc, &null);
int i;
oldarray = (ArrayType*)datum;
Assert(null || ARR_NDIM(oldarray) == 1);
/* first add the old array to the hitherto empty list */
if (!null)
for (i = ARR_LBOUND(oldarray)[0]; i < ARR_LBOUND(oldarray)[0] + ARR_DIMS(oldarray)[0]; i++)
{
int index, arrval;
Value *v;
bool valueNull;
index = i;
arrval = DatumGetInt32(array_ref(oldarray, 1, &index, true/*by value*/,
sizeof(int), 0, &valueNull));
v = makeInteger(arrval);
/* filter out duplicates */
if (!member(v, newlist))
newlist = lcons(v, newlist);
}
/*
* now convert the to be added usernames to sysids and add them
* to the list
*/
foreach(item, stmt->listUsers)
{
Value *v;
/* Get the uid of the proposed user to add. */
tuple = SearchSysCacheTuple(SHADOWNAME,
PointerGetDatum(strVal(lfirst(item))),
0, 0, 0);
if (!HeapTupleIsValid(tuple))
{
heap_close(pg_group_rel, AccessExclusiveLock);
UserAbortTransactionBlock();
elog(ERROR, "AlterGroup: User \"%s\" does not exist.", strVal(lfirst(item)));
}
v = makeInteger(((Form_pg_shadow) GETSTRUCT(tuple))->usesysid);
if (!member(v, newlist))
newlist = lcons(v, newlist);
else
elog(NOTICE, "AlterGroup: User \"%s\" is already in group \"%s\".", strVal(lfirst(item)), stmt->name);
}
newarray = palloc(ARR_OVERHEAD(1) + length(newlist) * sizeof(int32));
ARR_SIZE(newarray) = ARR_OVERHEAD(1) + length(newlist) * sizeof(int32);
ARR_FLAGS(newarray) = 0x0;
ARR_NDIM(newarray) = 1; /* one dimensional array */
ARR_LBOUND(newarray)[0] = 1; /* axis starts at one */
ARR_DIMS(newarray)[0] = length(newlist); /* axis is this long */
/* fill the array */
i = 0;
foreach(item, newlist)
{
((int*)ARR_DATA_PTR(newarray))[i++] = intVal(lfirst(item));
}
/*
* Form a tuple with the new array and write it back.
*/
new_record[Anum_pg_group_groname-1] = (Datum)(stmt->name);
new_record[Anum_pg_group_grosysid-1] = heap_getattr(group_tuple, Anum_pg_group_grosysid, pg_group_dsc, &null);
new_record[Anum_pg_group_grolist-1] = PointerGetDatum(newarray);
tuple = heap_formtuple(pg_group_dsc, new_record, new_record_nulls);
heap_update(pg_group_rel, &group_tuple->t_self, tuple, NULL);
/* Update indexes */
if (RelationGetForm(pg_group_rel)->relhasindex) {
Relation idescs[Num_pg_group_indices];
CatalogOpenIndices(Num_pg_group_indices,
Name_pg_group_indices, idescs);
CatalogIndexInsert(idescs, Num_pg_group_indices, pg_group_rel,
tuple);
CatalogCloseIndices(Num_pg_group_indices, idescs);
}
} /* endif alter group add user */
/*
* drop users from group
*/
else if (stmt->action < 0)
{
Datum datum;
bool null;
datum = heap_getattr(group_tuple, Anum_pg_group_grolist, pg_group_dsc, &null);
if (null)
elog(NOTICE, "AlterGroup: Group \"%s\"'s membership is NULL.", stmt->name);
else
{
HeapTuple tuple;
Datum new_record[Natts_pg_group];
char new_record_nulls[Natts_pg_group] = { ' ', ' ', ' '};
ArrayType *oldarray, *newarray;
List * newlist = NULL, *item;
int i;
oldarray = (ArrayType*)datum;
Assert(ARR_NDIM(oldarray) == 1);
/* first add the old array to the hitherto empty list */
for (i = ARR_LBOUND(oldarray)[0]; i < ARR_LBOUND(oldarray)[0] + ARR_DIMS(oldarray)[0]; i++)
{
int index, arrval;
Value *v;
bool valueNull;
index = i;
arrval = DatumGetInt32(array_ref(oldarray, 1, &index, true/*by value*/,
sizeof(int), 0, &valueNull));
v = makeInteger(arrval);
/* filter out duplicates */
if (!member(v, newlist))
newlist = lcons(v, newlist);
}
/*
* now convert the to be dropped usernames to sysids and remove
* them from the list
*/
foreach(item, stmt->listUsers)
{
Value *v;
/* Get the uid of the proposed user to drop. */
tuple = SearchSysCacheTuple(SHADOWNAME,
PointerGetDatum(strVal(lfirst(item))),
0, 0, 0);
if (!HeapTupleIsValid(tuple))
{
heap_close(pg_group_rel, AccessExclusiveLock);
UserAbortTransactionBlock();
elog(ERROR, "AlterGroup: User \"%s\" does not exist.", strVal(lfirst(item)));
}
v = makeInteger(((Form_pg_shadow) GETSTRUCT(tuple))->usesysid);
if (member(v, newlist))
newlist = LispRemove(v, newlist);
else
elog(NOTICE, "AlterGroup: User \"%s\" is not in group \"%s\".", strVal(lfirst(item)), stmt->name);
}
newarray = palloc(ARR_OVERHEAD(1) + length(newlist) * sizeof(int32));
ARR_SIZE(newarray) = ARR_OVERHEAD(1) + length(newlist) * sizeof(int32);
ARR_FLAGS(newarray) = 0x0;
ARR_NDIM(newarray) = 1; /* one dimensional array */
ARR_LBOUND(newarray)[0] = 1; /* axis starts at one */
ARR_DIMS(newarray)[0] = length(newlist); /* axis is this long */
/* fill the array */
i = 0;
foreach(item, newlist)
{
((int*)ARR_DATA_PTR(newarray))[i++] = intVal(lfirst(item));
}
/*
* Insert the new tuple with the updated user list
*/
new_record[Anum_pg_group_groname-1] = (Datum)(stmt->name);
new_record[Anum_pg_group_grosysid-1] = heap_getattr(group_tuple, Anum_pg_group_grosysid, pg_group_dsc, &null);
new_record[Anum_pg_group_grolist-1] = PointerGetDatum(newarray);
tuple = heap_formtuple(pg_group_dsc, new_record, new_record_nulls);
heap_update(pg_group_rel, &group_tuple->t_self, tuple, NULL);
/* Update indexes */
if (RelationGetForm(pg_group_rel)->relhasindex) {
Relation idescs[Num_pg_group_indices];
CatalogOpenIndices(Num_pg_group_indices,
Name_pg_group_indices, idescs);
CatalogIndexInsert(idescs, Num_pg_group_indices, pg_group_rel,
tuple);
CatalogCloseIndices(Num_pg_group_indices, idescs);
}
} /* endif group not null */
} /* endif alter group drop user */
heap_close(pg_group_rel, NoLock);
pfree(group_tuple);
if (IsTransactionBlock() && !inblock)
EndTransactionBlock();
}
void
DropGroup(DropGroupStmt *stmt, CommandDest dest)
{
Relation pg_group_rel;
HeapScanDesc scan;
HeapTuple tuple;
TupleDesc pg_group_dsc;
bool inblock;
bool gro_exists = false;
if (!(inblock = IsTransactionBlock()))
BeginTransactionBlock();
/*
* Make sure the user can do this.
*/
if (pg_aclcheck(GroupRelationName, GetPgUserName(), ACL_RD | ACL_WR) != ACLCHECK_OK)
{
UserAbortTransactionBlock();
elog(ERROR, "DropGroup: Permission denied.");
}
/*
* Scan the pg_group table and delete all matching users.
*/
pg_group_rel = heap_openr(GroupRelationName, AccessExclusiveLock);
pg_group_dsc = RelationGetDescr(pg_group_rel);
scan = heap_beginscan(pg_group_rel, false, SnapshotNow, 0, NULL);
while (HeapTupleIsValid(tuple = heap_getnext(scan, false)))
{
Datum datum;
bool null;
datum = heap_getattr(tuple, Anum_pg_group_groname, pg_group_dsc, &null);
if (datum && !null && strcmp((char*)datum, stmt->name)==0)
{
gro_exists = true;
heap_delete(pg_group_rel, &tuple->t_self, NULL);
}
}
heap_endscan(scan);
/*
* Did we find any?
*/
if (!gro_exists)
{
heap_close(pg_group_rel, AccessExclusiveLock);
UserAbortTransactionBlock();
elog(ERROR, "DropGroup: Group \"%s\" does not exist.", stmt->name);
}
heap_close(pg_group_rel, NoLock);
if (IsTransactionBlock() && !inblock)
EndTransactionBlock();
}