/*------------------------------------------------------------------------- * * 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.46 1999/12/20 01:11:37 tgl Exp $ * *------------------------------------------------------------------------- */ #include #include #include #include #include "postgres.h" #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" #include "commands/user.h" #include "libpq/crypt.h" #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); #define SQL_LENGTH 512 /*--------------------------------------------------------------------- * 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. *--------------------------------------------------------------------- */ void update_pg_pwd(void) { char *filename, *tempname; int bufsize; /* * 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(); 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); } /*--------------------------------------------------------------------- * 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) { 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, inblock, havesysid, 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. */ 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\"", 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); pg_shadow_dsc = RelationGetDescr(pg_shadow_rel); 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. * * 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(). */ 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. */ 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) { char *pg_shadow, sql[SQL_LENGTH]; Relation pg_shadow_rel; TupleDesc pg_shadow_dsc; HeapTuple tuple; 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. */ 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\"", 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); 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 */ 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); 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; } if (stmt->createdb) { if (comma) strcat(sql, ", "); snprintf(sql + strlen(sql), SQL_LENGTH - strlen(sql), "usecreatedb='%c'", *stmt->createdb ? 't' : 'f'); comma = true; } if (stmt->createuser) { if (comma) strcat(sql, ", "); snprintf(sql + strlen(sql), SQL_LENGTH - strlen(sql), "usesuper='%c'", *stmt->createuser ? 't' : 'f'); comma = true; } if (stmt->validUntil) { if (comma) strcat(sql, ", "); snprintf(sql + strlen(sql), SQL_LENGTH - strlen(sql), "valuntil='%s'", stmt->validUntil); } snprintf(sql + strlen(sql), SQL_LENGTH - strlen(sql), " where usename = '%s'", stmt->user); pg_exec_query_dest(sql, dest, false); /* * 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. */ 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) { 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. */ 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\"", 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); 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); pg_dsc = RelationGetDescr(pg_rel); 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) { dbase = (char **) repalloc((void *) dbase, sizeof(char *) * (ndbase + 1)); 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]); pfree((void *) dbase[ndbase]); pg_exec_query_dest(sql, dest, false); } if (dbase) 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 */ 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. */ 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(); }