diff options
Diffstat (limited to 'src/backend')
-rw-r--r-- | src/backend/catalog/Makefile | 6 | ||||
-rw-r--r-- | src/backend/catalog/catalog.c | 13 | ||||
-rw-r--r-- | src/backend/catalog/pg_db_role_setting.c | 231 | ||||
-rw-r--r-- | src/backend/catalog/pg_shdepend.c | 21 | ||||
-rw-r--r-- | src/backend/catalog/system_views.sql | 16 | ||||
-rw-r--r-- | src/backend/commands/dbcommands.c | 106 | ||||
-rw-r--r-- | src/backend/commands/user.c | 91 | ||||
-rw-r--r-- | src/backend/nodes/copyfuncs.c | 3 | ||||
-rw-r--r-- | src/backend/nodes/equalfuncs.c | 3 | ||||
-rw-r--r-- | src/backend/parser/gram.y | 37 | ||||
-rw-r--r-- | src/backend/utils/init/miscinit.c | 22 | ||||
-rw-r--r-- | src/backend/utils/init/postinit.c | 52 |
12 files changed, 388 insertions, 213 deletions
diff --git a/src/backend/catalog/Makefile b/src/backend/catalog/Makefile index 53784e9c54b..ec548990b10 100644 --- a/src/backend/catalog/Makefile +++ b/src/backend/catalog/Makefile @@ -2,7 +2,7 @@ # # Makefile for backend/catalog # -# $PostgreSQL: pgsql/src/backend/catalog/Makefile,v 1.72 2009/10/05 19:24:34 tgl Exp $ +# $PostgreSQL: pgsql/src/backend/catalog/Makefile,v 1.73 2009/10/07 22:14:16 alvherre Exp $ # #------------------------------------------------------------------------- @@ -13,7 +13,7 @@ include $(top_builddir)/src/Makefile.global OBJS = catalog.o dependency.o heap.o index.o indexing.o namespace.o aclchk.o \ pg_aggregate.o pg_constraint.o pg_conversion.o pg_depend.o pg_enum.o \ pg_inherits.o pg_largeobject.o pg_namespace.o pg_operator.o pg_proc.o \ - pg_shdepend.o pg_type.o storage.o toasting.o + pg_db_role_setting.o pg_shdepend.o pg_type.o storage.o toasting.o BKIFILES = postgres.bki postgres.description postgres.shdescription @@ -32,7 +32,7 @@ POSTGRES_BKI_SRCS = $(addprefix $(top_srcdir)/src/include/catalog/,\ pg_language.h pg_largeobject.h pg_aggregate.h pg_statistic.h \ pg_rewrite.h pg_trigger.h pg_listener.h pg_description.h pg_cast.h \ pg_enum.h pg_namespace.h pg_conversion.h pg_depend.h \ - pg_database.h pg_tablespace.h pg_pltemplate.h \ + pg_database.h pg_db_role_setting.h pg_tablespace.h pg_pltemplate.h \ pg_authid.h pg_auth_members.h pg_shdepend.h pg_shdescription.h \ pg_ts_config.h pg_ts_config_map.h pg_ts_dict.h \ pg_ts_parser.h pg_ts_template.h \ diff --git a/src/backend/catalog/catalog.c b/src/backend/catalog/catalog.c index 42371d51373..82d02f9609a 100644 --- a/src/backend/catalog/catalog.c +++ b/src/backend/catalog/catalog.c @@ -10,7 +10,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/catalog/catalog.c,v 1.83 2009/06/11 14:48:54 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/catalog/catalog.c,v 1.84 2009/10/07 22:14:18 alvherre Exp $ * *------------------------------------------------------------------------- */ @@ -31,6 +31,7 @@ #include "catalog/pg_database.h" #include "catalog/pg_namespace.h" #include "catalog/pg_pltemplate.h" +#include "catalog/pg_db_role_setting.h" #include "catalog/pg_shdepend.h" #include "catalog/pg_shdescription.h" #include "catalog/pg_tablespace.h" @@ -306,7 +307,8 @@ IsSharedRelation(Oid relationId) relationId == PLTemplateRelationId || relationId == SharedDescriptionRelationId || relationId == SharedDependRelationId || - relationId == TableSpaceRelationId) + relationId == TableSpaceRelationId || + relationId == DbRoleSettingRelationId) return true; /* These are their indexes (see indexing.h) */ if (relationId == AuthIdRolnameIndexId || @@ -320,7 +322,8 @@ IsSharedRelation(Oid relationId) relationId == SharedDependDependerIndexId || relationId == SharedDependReferenceIndexId || relationId == TablespaceOidIndexId || - relationId == TablespaceNameIndexId) + relationId == TablespaceNameIndexId || + relationId == DbRoleSettingDatidRolidIndexId) return true; /* These are their toast tables and toast indexes (see toasting.h) */ if (relationId == PgAuthidToastTable || @@ -328,7 +331,9 @@ IsSharedRelation(Oid relationId) relationId == PgDatabaseToastTable || relationId == PgDatabaseToastIndex || relationId == PgShdescriptionToastTable || - relationId == PgShdescriptionToastIndex) + relationId == PgShdescriptionToastIndex || + relationId == PgDbRoleSettingToastTable || + relationId == PgDbRoleSettingToastIndex) return true; return false; } diff --git a/src/backend/catalog/pg_db_role_setting.c b/src/backend/catalog/pg_db_role_setting.c new file mode 100644 index 00000000000..74870113d05 --- /dev/null +++ b/src/backend/catalog/pg_db_role_setting.c @@ -0,0 +1,231 @@ +/* + * pg_db_role_setting.c + * Routines to support manipulation of the pg_db_role_setting relation + * + * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * IDENTIFICATION + * $PostgreSQL: pgsql/src/backend/catalog/pg_db_role_setting.c,v 1.1 2009/10/07 22:14:18 alvherre Exp $ + */ +#include "postgres.h" + +#include "access/genam.h" +#include "access/heapam.h" +#include "access/htup.h" +#include "access/skey.h" +#include "catalog/indexing.h" +#include "catalog/pg_db_role_setting.h" +#include "utils/fmgroids.h" +#include "utils/rel.h" +#include "utils/tqual.h" + +void +AlterSetting(Oid databaseid, Oid roleid, VariableSetStmt *setstmt) +{ + char *valuestr; + HeapTuple tuple; + Relation rel; + ScanKeyData scankey[2]; + SysScanDesc scan; + + valuestr = ExtractSetVariableArgs(setstmt); + + /* Get the old tuple, if any. */ + + rel = heap_open(DbRoleSettingRelationId, RowExclusiveLock); + ScanKeyInit(&scankey[0], + Anum_pg_db_role_setting_setdatabase, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(databaseid)); + ScanKeyInit(&scankey[1], + Anum_pg_db_role_setting_setrole, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(roleid)); + scan = systable_beginscan(rel, DbRoleSettingDatidRolidIndexId, true, + SnapshotNow, 2, scankey); + tuple = systable_getnext(scan); + + /* + * There are three cases: + * + * - in RESET ALL, simply delete the pg_db_role_setting tuple (if any) + * + * - in other commands, if there's a tuple in pg_db_role_setting, update it; + * if it ends up empty, delete it + * + * - otherwise, insert a new pg_db_role_setting tuple, but only if the + * command is not RESET + */ + if (setstmt->kind == VAR_RESET_ALL) + { + if (HeapTupleIsValid(tuple)) + simple_heap_delete(rel, &tuple->t_self); + } + else if (HeapTupleIsValid(tuple)) + { + Datum repl_val[Natts_pg_db_role_setting]; + bool repl_null[Natts_pg_db_role_setting]; + bool repl_repl[Natts_pg_db_role_setting]; + HeapTuple newtuple; + Datum datum; + bool isnull; + ArrayType *a; + + memset(repl_repl, false, sizeof(repl_repl)); + repl_repl[Anum_pg_db_role_setting_setconfig - 1] = true; + repl_null[Anum_pg_db_role_setting_setconfig - 1] = false; + + /* Extract old value of setconfig */ + datum = heap_getattr(tuple, Anum_pg_db_role_setting_setconfig, + RelationGetDescr(rel), &isnull); + a = isnull ? NULL : DatumGetArrayTypeP(datum); + + /* Update (valuestr is NULL in RESET cases) */ + if (valuestr) + a = GUCArrayAdd(a, setstmt->name, valuestr); + else + a = GUCArrayDelete(a, setstmt->name); + + if (a) + { + repl_val[Anum_pg_db_role_setting_setconfig - 1] = + PointerGetDatum(a); + + newtuple = heap_modify_tuple(tuple, RelationGetDescr(rel), + repl_val, repl_null, repl_repl); + simple_heap_update(rel, &tuple->t_self, newtuple); + + /* Update indexes */ + CatalogUpdateIndexes(rel, newtuple); + } + else + simple_heap_delete(rel, &tuple->t_self); + } + else if (valuestr) + { + /* non-null valuestr means it's not RESET, so insert a new tuple */ + HeapTuple newtuple; + Datum values[Natts_pg_db_role_setting]; + bool nulls[Natts_pg_db_role_setting]; + ArrayType *a; + + memset(nulls, false, sizeof(nulls)); + + a = GUCArrayAdd(NULL, setstmt->name, valuestr); + + values[Anum_pg_db_role_setting_setdatabase - 1] = + ObjectIdGetDatum(databaseid); + values[Anum_pg_db_role_setting_setrole - 1] = ObjectIdGetDatum(roleid); + values[Anum_pg_db_role_setting_setconfig - 1] = PointerGetDatum(a); + newtuple = heap_form_tuple(RelationGetDescr(rel), values, nulls); + + simple_heap_insert(rel, newtuple); + + /* Update indexes */ + CatalogUpdateIndexes(rel, newtuple); + } + + systable_endscan(scan); + + /* Close pg_db_role_setting, but keep lock till commit */ + heap_close(rel, NoLock); +} + +/* + * Drop some settings from the catalog. These can be for a particular + * database, or for a particular role. (It is of course possible to do both + * too, but it doesn't make sense for current uses.) + */ +void +DropSetting(Oid databaseid, Oid roleid) +{ + Relation relsetting; + HeapScanDesc scan; + ScanKeyData keys[2]; + HeapTuple tup; + int numkeys = 0; + + relsetting = heap_open(DbRoleSettingRelationId, RowExclusiveLock); + + if (OidIsValid(databaseid)) + { + ScanKeyInit(&keys[numkeys], + Anum_pg_db_role_setting_setdatabase, + BTEqualStrategyNumber, + F_OIDEQ, + ObjectIdGetDatum(databaseid)); + numkeys++; + } + if (OidIsValid(roleid)) + { + ScanKeyInit(&keys[numkeys], + Anum_pg_db_role_setting_setrole, + BTEqualStrategyNumber, + F_OIDEQ, + ObjectIdGetDatum(roleid)); + numkeys++; + } + + scan = heap_beginscan(relsetting, SnapshotNow, numkeys, keys); + while (HeapTupleIsValid(tup = heap_getnext(scan, ForwardScanDirection))) + { + simple_heap_delete(relsetting, &tup->t_self); + } + heap_endscan(scan); + + heap_close(relsetting, RowExclusiveLock); +} + +/* + * Scan pg_db_role_setting looking for applicable settings, and load them on + * the current process. + * + * relsetting is pg_db_role_setting, already opened and locked. + * + * Note: we only consider setting for the exact databaseid/roleid combination. + * This probably needs to be called more than once, with InvalidOid passed as + * databaseid/roleid. + */ +void +ApplySetting(Oid databaseid, Oid roleid, Relation relsetting, GucSource source) +{ + SysScanDesc scan; + ScanKeyData keys[2]; + HeapTuple tup; + + ScanKeyInit(&keys[0], + Anum_pg_db_role_setting_setdatabase, + BTEqualStrategyNumber, + F_OIDEQ, + ObjectIdGetDatum(databaseid)); + ScanKeyInit(&keys[1], + Anum_pg_db_role_setting_setrole, + BTEqualStrategyNumber, + F_OIDEQ, + ObjectIdGetDatum(roleid)); + + scan = systable_beginscan(relsetting, DbRoleSettingDatidRolidIndexId, true, + SnapshotNow, 2, keys); + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + bool isnull; + Datum datum; + + datum = heap_getattr(tup, Anum_pg_db_role_setting_setconfig, + RelationGetDescr(relsetting), &isnull); + if (!isnull) + { + ArrayType *a = DatumGetArrayTypeP(datum); + + /* + * We process all the options at SUSET level. We assume that the + * right to insert an option into pg_db_role_setting was checked + * when it was inserted. + */ + ProcessGUCArray(a, PGC_SUSET, source, GUC_ACTION_SET); + } + } + + systable_endscan(scan); +} diff --git a/src/backend/catalog/pg_shdepend.c b/src/backend/catalog/pg_shdepend.c index 869ec1fdd57..be70143ea27 100644 --- a/src/backend/catalog/pg_shdepend.c +++ b/src/backend/catalog/pg_shdepend.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/catalog/pg_shdepend.c,v 1.35 2009/10/05 19:24:36 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/catalog/pg_shdepend.c,v 1.36 2009/10/07 22:14:18 alvherre Exp $ * *------------------------------------------------------------------------- */ @@ -31,6 +31,7 @@ #include "catalog/pg_shdepend.h" #include "catalog/pg_tablespace.h" #include "catalog/pg_type.h" +#include "commands/dbcommands.h" #include "commands/conversioncmds.h" #include "commands/defrem.h" #include "commands/proclang.h" @@ -55,7 +56,6 @@ typedef enum static int getOidListDiff(Oid *list1, int nlist1, Oid *list2, int nlist2, Oid **diff); static Oid classIdGetDbId(Oid classId); -static void shdepLockAndCheckObject(Oid classId, Oid objectId); static void shdepChangeDep(Relation sdepRel, Oid classid, Oid objid, int32 objsubid, Oid refclassid, Oid refobjid, @@ -963,7 +963,7 @@ classIdGetDbId(Oid classId) * weren't looking. If the object has been dropped, this function * does not return! */ -static void +void shdepLockAndCheckObject(Oid classId, Oid objectId) { /* AccessShareLock should be OK, since we are not modifying the object */ @@ -1003,6 +1003,21 @@ shdepLockAndCheckObject(Oid classId, Oid objectId) } #endif + case DatabaseRelationId: + { + /* For lack of a syscache on pg_database, do this: */ + char *database = get_database_name(objectId); + + if (database == NULL) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("database %u was concurrently dropped", + objectId))); + pfree(database); + break; + } + + default: elog(ERROR, "unrecognized shared classId: %u", classId); } diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql index 73e391c8968..c2fbfcdf0a3 100644 --- a/src/backend/catalog/system_views.sql +++ b/src/backend/catalog/system_views.sql @@ -3,7 +3,7 @@ * * Copyright (c) 1996-2009, PostgreSQL Global Development Group * - * $PostgreSQL: pgsql/src/backend/catalog/system_views.sql,v 1.60 2009/04/07 00:31:26 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/catalog/system_views.sql,v 1.61 2009/10/07 22:14:18 alvherre Exp $ */ CREATE VIEW pg_roles AS @@ -18,21 +18,23 @@ CREATE VIEW pg_roles AS rolconnlimit, '********'::text as rolpassword, rolvaliduntil, - rolconfig, - oid - FROM pg_authid; + setconfig as rolconfig, + pg_authid.oid + FROM pg_authid LEFT JOIN pg_db_role_setting s + ON (pg_authid.oid = setrole AND setdatabase = 0); CREATE VIEW pg_shadow AS SELECT rolname AS usename, - oid AS usesysid, + pg_authid.oid AS usesysid, rolcreatedb AS usecreatedb, rolsuper AS usesuper, rolcatupdate AS usecatupd, rolpassword AS passwd, rolvaliduntil::abstime AS valuntil, - rolconfig AS useconfig - FROM pg_authid + setconfig AS useconfig + FROM pg_authid LEFT JOIN pg_db_role_setting s + ON (pg_authid.oid = setrole AND setdatabase = 0) WHERE rolcanlogin; REVOKE ALL on pg_shadow FROM public; diff --git a/src/backend/commands/dbcommands.c b/src/backend/commands/dbcommands.c index 2e6edc4832e..7df44c9ec41 100644 --- a/src/backend/commands/dbcommands.c +++ b/src/backend/commands/dbcommands.c @@ -13,7 +13,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/dbcommands.c,v 1.226 2009/09/01 02:54:51 alvherre Exp $ + * $PostgreSQL: pgsql/src/backend/commands/dbcommands.c,v 1.227 2009/10/07 22:14:18 alvherre Exp $ * *------------------------------------------------------------------------- */ @@ -33,6 +33,7 @@ #include "catalog/indexing.h" #include "catalog/pg_authid.h" #include "catalog/pg_database.h" +#include "catalog/pg_db_role_setting.h" #include "catalog/pg_tablespace.h" #include "commands/comment.h" #include "commands/dbcommands.h" @@ -50,7 +51,6 @@ #include "utils/acl.h" #include "utils/builtins.h" #include "utils/fmgroids.h" -#include "utils/guc.h" #include "utils/lsyscache.h" #include "utils/pg_locale.h" #include "utils/snapmgr.h" @@ -544,12 +544,10 @@ createdb(const CreatedbStmt *stmt) new_record[Anum_pg_database_dattablespace - 1] = ObjectIdGetDatum(dst_deftablespace); /* - * We deliberately set datconfig and datacl to defaults (NULL), rather - * than copying them from the template database. Copying datacl would be - * a bad idea when the owner is not the same as the template's owner. It's - * more debatable whether datconfig should be copied. + * We deliberately set datacl to default (NULL), rather than copying it + * from the template database. Copying it would be a bad idea when the + * owner is not the same as the template's owner. */ - new_record_nulls[Anum_pg_database_datconfig - 1] = true; new_record_nulls[Anum_pg_database_datacl - 1] = true; tuple = heap_form_tuple(RelationGetDescr(pg_database_rel), @@ -821,6 +819,11 @@ dropdb(const char *dbname, bool missing_ok) DeleteSharedComments(db_id, DatabaseRelationId); /* + * Remove settings associated with this database + */ + DropSetting(db_id, InvalidOid); + + /* * Remove shared dependency references for the database. */ dropDatabaseDependencies(db_id); @@ -1397,85 +1400,26 @@ AlterDatabase(AlterDatabaseStmt *stmt, bool isTopLevel) void AlterDatabaseSet(AlterDatabaseSetStmt *stmt) { - char *valuestr; - HeapTuple tuple, - newtuple; - Relation rel; - ScanKeyData scankey; - SysScanDesc scan; - Datum repl_val[Natts_pg_database]; - bool repl_null[Natts_pg_database]; - bool repl_repl[Natts_pg_database]; - - valuestr = ExtractSetVariableArgs(stmt->setstmt); + Oid datid = get_database_oid(stmt->dbname); + if (!OidIsValid(datid)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_DATABASE), + errmsg("database \"%s\" does not exist", stmt->dbname))); + /* - * Get the old tuple. We don't need a lock on the database per se, - * because we're not going to do anything that would mess up incoming - * connections. + * Obtain a lock on the database and make sure it didn't go away in the + * meantime. */ - rel = heap_open(DatabaseRelationId, RowExclusiveLock); - ScanKeyInit(&scankey, - Anum_pg_database_datname, - BTEqualStrategyNumber, F_NAMEEQ, - NameGetDatum(stmt->dbname)); - scan = systable_beginscan(rel, DatabaseNameIndexId, true, - SnapshotNow, 1, &scankey); - tuple = systable_getnext(scan); - if (!HeapTupleIsValid(tuple)) - ereport(ERROR, - (errcode(ERRCODE_UNDEFINED_DATABASE), - errmsg("database \"%s\" does not exist", stmt->dbname))); + shdepLockAndCheckObject(DatabaseRelationId, datid); - if (!pg_database_ownercheck(HeapTupleGetOid(tuple), GetUserId())) - aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_DATABASE, - stmt->dbname); - - memset(repl_repl, false, sizeof(repl_repl)); - repl_repl[Anum_pg_database_datconfig - 1] = true; - - if (stmt->setstmt->kind == VAR_RESET_ALL) - { - /* RESET ALL, so just set datconfig to null */ - repl_null[Anum_pg_database_datconfig - 1] = true; - repl_val[Anum_pg_database_datconfig - 1] = (Datum) 0; - } - else - { - Datum datum; - bool isnull; - ArrayType *a; - - repl_null[Anum_pg_database_datconfig - 1] = false; - - /* Extract old value of datconfig */ - datum = heap_getattr(tuple, Anum_pg_database_datconfig, - RelationGetDescr(rel), &isnull); - a = isnull ? NULL : DatumGetArrayTypeP(datum); - - /* Update (valuestr is NULL in RESET cases) */ - if (valuestr) - a = GUCArrayAdd(a, stmt->setstmt->name, valuestr); - else - a = GUCArrayDelete(a, stmt->setstmt->name); + if (!pg_database_ownercheck(datid, GetUserId())) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_DATABASE, + stmt->dbname); - if (a) - repl_val[Anum_pg_database_datconfig - 1] = PointerGetDatum(a); - else - repl_null[Anum_pg_database_datconfig - 1] = true; - } - - newtuple = heap_modify_tuple(tuple, RelationGetDescr(rel), - repl_val, repl_null, repl_repl); - simple_heap_update(rel, &tuple->t_self, newtuple); - - /* Update indexes */ - CatalogUpdateIndexes(rel, newtuple); - - systable_endscan(scan); - - /* Close pg_database, but keep lock till commit */ - heap_close(rel, NoLock); + AlterSetting(datid, InvalidOid, stmt->setstmt); + + UnlockSharedObject(DatabaseRelationId, datid, 0, AccessShareLock); } diff --git a/src/backend/commands/user.c b/src/backend/commands/user.c index c157ead4726..ef546cf3602 100644 --- a/src/backend/commands/user.c +++ b/src/backend/commands/user.c @@ -6,7 +6,7 @@ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/backend/commands/user.c,v 1.188 2009/09/01 02:54:51 alvherre Exp $ + * $PostgreSQL: pgsql/src/backend/commands/user.c,v 1.189 2009/10/07 22:14:19 alvherre Exp $ * *------------------------------------------------------------------------- */ @@ -19,7 +19,10 @@ #include "catalog/indexing.h" #include "catalog/pg_auth_members.h" #include "catalog/pg_authid.h" +#include "catalog/pg_database.h" +#include "catalog/pg_db_role_setting.h" #include "commands/comment.h" +#include "commands/dbcommands.h" #include "commands/user.h" #include "libpq/md5.h" #include "miscadmin.h" @@ -27,7 +30,6 @@ #include "utils/acl.h" #include "utils/builtins.h" #include "utils/fmgroids.h" -#include "utils/guc.h" #include "utils/lsyscache.h" #include "utils/syscache.h" #include "utils/tqual.h" @@ -341,8 +343,6 @@ CreateRole(CreateRoleStmt *stmt) else new_record_nulls[Anum_pg_authid_rolvaliduntil - 1] = true; - new_record_nulls[Anum_pg_authid_rolconfig - 1] = true; - tuple = heap_form_tuple(pg_authid_dsc, new_record, new_record_nulls); /* @@ -715,30 +715,29 @@ AlterRole(AlterRoleStmt *stmt) void AlterRoleSet(AlterRoleSetStmt *stmt) { - char *valuestr; - HeapTuple oldtuple, - newtuple; - Relation rel; - Datum repl_val[Natts_pg_authid]; - bool repl_null[Natts_pg_authid]; - bool repl_repl[Natts_pg_authid]; + HeapTuple roletuple; + Oid databaseid = InvalidOid; - valuestr = ExtractSetVariableArgs(stmt->setstmt); + roletuple = SearchSysCache(AUTHNAME, + PointerGetDatum(stmt->role), + 0, 0, 0); - rel = heap_open(AuthIdRelationId, RowExclusiveLock); - oldtuple = SearchSysCache(AUTHNAME, - PointerGetDatum(stmt->role), - 0, 0, 0); - if (!HeapTupleIsValid(oldtuple)) + if (!HeapTupleIsValid(roletuple)) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("role \"%s\" does not exist", stmt->role))); /* + * Obtain a lock on the role and make sure it didn't go away in the + * meantime. + */ + shdepLockAndCheckObject(AuthIdRelationId, HeapTupleGetOid(roletuple)); + + /* * To mess with a superuser you gotta be superuser; else you need * createrole, or just want to change your own settings */ - if (((Form_pg_authid) GETSTRUCT(oldtuple))->rolsuper) + if (((Form_pg_authid) GETSTRUCT(roletuple))->rolsuper) { if (!superuser()) ereport(ERROR, @@ -748,54 +747,25 @@ AlterRoleSet(AlterRoleSetStmt *stmt) else { if (!have_createrole_privilege() && - HeapTupleGetOid(oldtuple) != GetUserId()) + HeapTupleGetOid(roletuple) != GetUserId()) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("permission denied"))); } - memset(repl_repl, false, sizeof(repl_repl)); - repl_repl[Anum_pg_authid_rolconfig - 1] = true; - - if (stmt->setstmt->kind == VAR_RESET_ALL) + /* look up and lock the database, if specified */ + if (stmt->database != NULL) { - /* RESET ALL, so just set rolconfig to null */ - repl_null[Anum_pg_authid_rolconfig - 1] = true; - repl_val[Anum_pg_authid_rolconfig - 1] = (Datum) 0; - } - else - { - Datum datum; - bool isnull; - ArrayType *array; - - repl_null[Anum_pg_authid_rolconfig - 1] = false; - - /* Extract old value of rolconfig */ - datum = SysCacheGetAttr(AUTHNAME, oldtuple, - Anum_pg_authid_rolconfig, &isnull); - array = isnull ? NULL : DatumGetArrayTypeP(datum); - - /* Update (valuestr is NULL in RESET cases) */ - if (valuestr) - array = GUCArrayAdd(array, stmt->setstmt->name, valuestr); - else - array = GUCArrayDelete(array, stmt->setstmt->name); - - if (array) - repl_val[Anum_pg_authid_rolconfig - 1] = PointerGetDatum(array); - else - repl_null[Anum_pg_authid_rolconfig - 1] = true; + databaseid = get_database_oid(stmt->database); + if (!OidIsValid(databaseid)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("database \"%s\" not found", stmt->database))); + shdepLockAndCheckObject(DatabaseRelationId, databaseid); } - newtuple = heap_modify_tuple(oldtuple, RelationGetDescr(rel), - repl_val, repl_null, repl_repl); - - simple_heap_update(rel, &oldtuple->t_self, newtuple); - CatalogUpdateIndexes(rel, newtuple); - - ReleaseSysCache(oldtuple); - heap_close(rel, RowExclusiveLock); + AlterSetting(databaseid, HeapTupleGetOid(roletuple), stmt->setstmt); + ReleaseSysCache(roletuple); } @@ -944,6 +914,11 @@ DropRole(DropRoleStmt *stmt) DeleteSharedComments(roleid, AuthIdRelationId); /* + * Remove settings for this role. + */ + DropSetting(InvalidOid, roleid); + + /* * Advance command counter so that later iterations of this loop will * see the changes already made. This is essential if, for example, * we are trying to drop both a role and one of its direct members --- diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index 7c3cb049b09..9319aa84c5e 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 - * $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.440 2009/10/06 00:55:26 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.441 2009/10/07 22:14:20 alvherre Exp $ * *------------------------------------------------------------------------- */ @@ -3174,6 +3174,7 @@ _copyAlterRoleSetStmt(AlterRoleSetStmt *from) AlterRoleSetStmt *newnode = makeNode(AlterRoleSetStmt); COPY_STRING_FIELD(role); + COPY_STRING_FIELD(database); COPY_NODE_FIELD(setstmt); return newnode; diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c index bf978f871de..6a61112b99c 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -22,7 +22,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.363 2009/10/06 00:55:26 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.364 2009/10/07 22:14:20 alvherre Exp $ * *------------------------------------------------------------------------- */ @@ -1725,6 +1725,7 @@ static bool _equalAlterRoleSetStmt(AlterRoleSetStmt *a, AlterRoleSetStmt *b) { COMPARE_STRING_FIELD(role); + COMPARE_STRING_FIELD(database); COMPARE_NODE_FIELD(setstmt); return true; diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index af2f080b634..ed265f516b2 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -11,7 +11,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.680 2009/10/05 19:24:38 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.681 2009/10/07 22:14:21 alvherre Exp $ * * HISTORY * AUTHOR DATE MAJOR EVENT @@ -237,12 +237,13 @@ static TypeName *TableFuncTypeName(List *columns); opt_grant_grant_option opt_grant_admin_option opt_nowait opt_if_exists opt_with_data -%type <list> OptRoleList -%type <defelt> OptRoleElem +%type <list> OptRoleList AlterOptRoleList +%type <defelt> CreateOptRoleElem AlterOptRoleElem %type <str> opt_type %type <str> foreign_server_version opt_foreign_server_version %type <str> auth_ident +%type <str> opt_in_database %type <str> OptSchemaName %type <list> OptSchemaEltList @@ -762,11 +763,16 @@ opt_with: WITH {} * is "WITH ADMIN name". */ OptRoleList: - OptRoleList OptRoleElem { $$ = lappend($1, $2); } + OptRoleList CreateOptRoleElem { $$ = lappend($1, $2); } | /* EMPTY */ { $$ = NIL; } ; -OptRoleElem: +AlterOptRoleList: + AlterOptRoleList AlterOptRoleElem { $$ = lappend($1, $2); } + | /* EMPTY */ { $$ = NIL; } + ; + +AlterOptRoleElem: PASSWORD Sconst { $$ = makeDefElem("password", @@ -848,7 +854,11 @@ OptRoleElem: { $$ = makeDefElem("rolemembers", (Node *)$2); } - /* The following are not supported by ALTER ROLE/USER/GROUP */ + ; + +CreateOptRoleElem: + AlterOptRoleElem { $$ = $1; } + /* The following are not supported by ALTER ROLE/USER/GROUP */ | SYSID Iconst { $$ = makeDefElem("sysid", (Node *)makeInteger($2)); @@ -897,7 +907,7 @@ CreateUserStmt: *****************************************************************************/ AlterRoleStmt: - ALTER ROLE RoleId opt_with OptRoleList + ALTER ROLE RoleId opt_with AlterOptRoleList { AlterRoleStmt *n = makeNode(AlterRoleStmt); n->role = $3; @@ -907,12 +917,18 @@ AlterRoleStmt: } ; +opt_in_database: + /* EMPTY */ { $$ = NULL; } + | IN_P DATABASE database_name { $$ = $3; } + ; + AlterRoleSetStmt: - ALTER ROLE RoleId SetResetClause + ALTER ROLE RoleId opt_in_database SetResetClause { AlterRoleSetStmt *n = makeNode(AlterRoleSetStmt); n->role = $3; - n->setstmt = $4; + n->database = $4; + n->setstmt = $5; $$ = (Node *)n; } ; @@ -925,7 +941,7 @@ AlterRoleSetStmt: *****************************************************************************/ AlterUserStmt: - ALTER USER RoleId opt_with OptRoleList + ALTER USER RoleId opt_with AlterOptRoleList { AlterRoleStmt *n = makeNode(AlterRoleStmt); n->role = $3; @@ -941,6 +957,7 @@ AlterUserSetStmt: { AlterRoleSetStmt *n = makeNode(AlterRoleSetStmt); n->role = $3; + n->database = NULL; n->setstmt = $4; $$ = (Node *)n; } diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index 85f1507ba36..f7a27b9e430 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/init/miscinit.c,v 1.177 2009/08/27 16:59:38 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/utils/init/miscinit.c,v 1.178 2009/10/07 22:14:22 alvherre Exp $ * *------------------------------------------------------------------------- */ @@ -392,8 +392,6 @@ InitializeSessionUserId(const char *rolename) { HeapTuple roleTup; Form_pg_authid rform; - Datum datum; - bool isnull; Oid roleid; /* @@ -470,24 +468,6 @@ InitializeSessionUserId(const char *rolename) AuthenticatedUserIsSuperuser ? "on" : "off", PGC_INTERNAL, PGC_S_OVERRIDE); - /* - * Set up user-specific configuration variables. This is a good place to - * do it so we don't have to read pg_authid twice during session startup. - */ - datum = SysCacheGetAttr(AUTHNAME, roleTup, - Anum_pg_authid_rolconfig, &isnull); - if (!isnull) - { - ArrayType *a = DatumGetArrayTypeP(datum); - - /* - * We process all the options at SUSET level. We assume that the - * right to insert an option into pg_authid was checked when it was - * inserted. - */ - ProcessGUCArray(a, PGC_SUSET, PGC_S_USER, GUC_ACTION_SET); - } - ReleaseSysCache(roleTup); } diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c index 5321afc1b8d..b6c93c7f8eb 100644 --- a/src/backend/utils/init/postinit.c +++ b/src/backend/utils/init/postinit.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/init/postinit.c,v 1.197 2009/09/01 00:09:42 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/utils/init/postinit.c,v 1.198 2009/10/07 22:14:23 alvherre Exp $ * * *------------------------------------------------------------------------- @@ -27,6 +27,7 @@ #include "catalog/namespace.h" #include "catalog/pg_authid.h" #include "catalog/pg_database.h" +#include "catalog/pg_db_role_setting.h" #include "catalog/pg_tablespace.h" #include "libpq/auth.h" #include "libpq/libpq-be.h" @@ -63,6 +64,7 @@ static void CheckMyDatabase(const char *name, bool am_superuser); static void InitCommunication(void); static void ShutdownPostgres(int code, Datum arg); static bool ThereIsAtLeastOneRole(void); +static void process_settings(Oid databaseid, Oid roleid); /*** InitPostgres support ***/ @@ -344,29 +346,6 @@ CheckMyDatabase(const char *name, bool am_superuser) pg_bind_textdomain_codeset(textdomain(NULL)); #endif - /* - * Lastly, set up any database-specific configuration variables. - */ - if (IsUnderPostmaster) - { - Datum datum; - bool isnull; - - datum = SysCacheGetAttr(DATABASEOID, tup, Anum_pg_database_datconfig, - &isnull); - if (!isnull) - { - ArrayType *a = DatumGetArrayTypeP(datum); - - /* - * We process all the options at SUSET level. We assume that the - * right to insert an option into pg_database was checked when it - * was inserted. - */ - ProcessGUCArray(a, PGC_SUSET, PGC_S_DATABASE, GUC_ACTION_SET); - } - } - ReleaseSysCache(tup); } @@ -739,6 +718,9 @@ InitPostgres(const char *in_dbname, Oid dboid, const char *username, /* set up ACL framework (so CheckMyDatabase can check permissions) */ initialize_acl(); + /* Process pg_db_role_setting options */ + process_settings(MyDatabaseId, GetSessionUserId()); + /* * Re-read the pg_database row for our database, check permissions and * set up database-specific GUC settings. We can't do this until all the @@ -851,6 +833,28 @@ InitPostgres(const char *in_dbname, Oid dboid, const char *username, CommitTransactionCommand(); } +/* + * Load GUC settings from pg_db_role_setting. + * + * We try specific settings for the database/role combination, as well as + * general for this database and for this user. + */ +static void +process_settings(Oid databaseid, Oid roleid) +{ + Relation relsetting; + + if (!IsUnderPostmaster) + return; + + relsetting = heap_open(DbRoleSettingRelationId, AccessShareLock); + + ApplySetting(databaseid, roleid, relsetting, PGC_S_DATABASE_USER); + ApplySetting(InvalidOid, roleid, relsetting, PGC_S_USER); + ApplySetting(databaseid, InvalidOid, relsetting, PGC_S_DATABASE); + + heap_close(relsetting, AccessShareLock); +} /* * Backend-shutdown callback. Do cleanup that we want to be sure happens |