diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml index 0c8ed68195..d8ec4f9f65 100644 --- a/doc/src/sgml/catalogs.sgml +++ b/doc/src/sgml/catalogs.sgml @@ -1,6 +1,6 @@ @@ -1018,6 +1018,16 @@ + + rolconnlimit + int4 + + + For roles that can log in, this sets maximum number of concurrent + connections this role can make. -1 means no limit. + + + rolpassword text @@ -1921,6 +1931,16 @@ + + datconnlimit + int4 + + + Sets maximum number of concurrent connections that can be made + to this database. -1 means no limit. + + + datlastsysoid oid @@ -4811,6 +4831,16 @@ + + rolconnlimit + int4 + + + For roles that can log in, this sets maximum number of concurrent + connections this role can make. -1 means no limit. + + + rolpassword text diff --git a/doc/src/sgml/ref/alter_database.sgml b/doc/src/sgml/ref/alter_database.sgml index c209308716..03333d3d99 100644 --- a/doc/src/sgml/ref/alter_database.sgml +++ b/doc/src/sgml/ref/alter_database.sgml @@ -1,5 +1,5 @@ @@ -20,6 +20,12 @@ PostgreSQL documentation +ALTER DATABASE name [ [ WITH ] option [ ... ] ] + +where option can be: + + CONNECTION LIMIT connlimit + ALTER DATABASE name SET parameter { TO | = } { value | DEFAULT } ALTER DATABASE name RESET parameter @@ -38,7 +44,12 @@ ALTER DATABASE name OWNER TO - The first two forms change the session default for a run-time + The first form changes certain per-database settings. (See below for + details.) Only the database owner or a superuser can change these settings. + + + + The second and third forms change the session default for a run-time configuration variable for a PostgreSQL database. Whenever a new session is subsequently started in that database, the specified value becomes the session default value. @@ -51,7 +62,7 @@ ALTER DATABASE name OWNER TO - The third form changes the name of the database. Only the database + The fourth form changes the name of the database. Only the database owner or a superuser can rename a database; non-superuser owners must also have the CREATEDB privilege. The current database cannot @@ -60,7 +71,7 @@ ALTER DATABASE name OWNER TO - The fourth form changes the owner of the database. Only a superuser + The fifth form changes the owner of the database. Only a superuser can change the database's owner. @@ -78,6 +89,16 @@ ALTER DATABASE name OWNER TO + + connlimit + + + How many concurrent connections can be made + to this database. -1 means no limit. + + + + parameter value diff --git a/doc/src/sgml/ref/alter_role.sgml b/doc/src/sgml/ref/alter_role.sgml index 3e87cf0176..7ebd8014f2 100644 --- a/doc/src/sgml/ref/alter_role.sgml +++ b/doc/src/sgml/ref/alter_role.sgml @@ -1,5 +1,5 @@ @@ -30,6 +30,7 @@ where option can be: | CREATEUSER | NOCREATEUSER | INHERIT | NOINHERIT | LOGIN | NOLOGIN + | CONNECTION LIMIT connlimit | [ ENCRYPTED | UNENCRYPTED ] PASSWORD 'password' | VALID UNTIL 'timestamp' @@ -118,6 +119,7 @@ ALTER ROLE name RESET NOINHERIT LOGIN NOLOGIN + CONNECTION LIMIT connlimit PASSWORD password ENCRYPTED UNENCRYPTED diff --git a/doc/src/sgml/ref/alter_user.sgml b/doc/src/sgml/ref/alter_user.sgml index 2844a63d08..d5dc3f1fca 100644 --- a/doc/src/sgml/ref/alter_user.sgml +++ b/doc/src/sgml/ref/alter_user.sgml @@ -1,5 +1,5 @@ @@ -30,6 +30,7 @@ where option can be: | CREATEUSER | NOCREATEUSER | INHERIT | NOINHERIT | LOGIN | NOLOGIN + | CONNECTION LIMIT connlimit | [ ENCRYPTED | UNENCRYPTED ] PASSWORD 'password' | VALID UNTIL 'timestamp' diff --git a/doc/src/sgml/ref/create_database.sgml b/doc/src/sgml/ref/create_database.sgml index edb98b784b..b4bd2d5739 100644 --- a/doc/src/sgml/ref/create_database.sgml +++ b/doc/src/sgml/ref/create_database.sgml @@ -1,5 +1,5 @@ @@ -24,7 +24,8 @@ CREATE DATABASE name [ [ WITH ] [ OWNER [=] dbowner ] [ TEMPLATE [=] template ] [ ENCODING [=] encoding ] - [ TABLESPACE [=] tablespace ] ] + [ TABLESPACE [=] tablespace ] + [ CONNECTION LIMIT [=] connlimit ] ] @@ -123,6 +124,16 @@ CREATE DATABASE name + + + connlimit + + + How many concurrent connections can be made + to this database. -1 (the default) means no limit. + + + @@ -161,6 +172,13 @@ CREATE DATABASE name We recommend that databases used as templates be treated as read-only. See for more information. + + + The CONNECTION LIMIT option is only enforced approximately; + if two new sessions start at about the same time when just one + connection slot remains for the database, it is possible that + both will fail. Also, the limit is not enforced against superusers. + diff --git a/doc/src/sgml/ref/create_role.sgml b/doc/src/sgml/ref/create_role.sgml index 598888528f..4cff62a6ec 100644 --- a/doc/src/sgml/ref/create_role.sgml +++ b/doc/src/sgml/ref/create_role.sgml @@ -1,5 +1,5 @@ @@ -30,6 +30,7 @@ where option can be: | CREATEUSER | NOCREATEUSER | INHERIT | NOINHERIT | LOGIN | NOLOGIN + | CONNECTION LIMIT connlimit | [ ENCRYPTED | UNENCRYPTED ] PASSWORD 'password' | VALID UNTIL 'timestamp' | IN ROLE rolename [, ...] @@ -172,6 +173,16 @@ where option can be: + + CONNECTION LIMIT connlimit + + + If role can log in, this specifies how many concurrent connections + the role can make. -1 (the default) means no limit. + + + + PASSWORD password @@ -327,6 +338,13 @@ where option can be: the same functionality as CREATE ROLE (in fact, it calls this command) but can be run from the command shell. + + + The CONNECTION LIMIT option is only enforced approximately; + if two new sessions start at about the same time when just one + connection slot remains for the role, it is possible that + both will fail. Also, the limit is never enforced for superusers. + diff --git a/doc/src/sgml/ref/create_user.sgml b/doc/src/sgml/ref/create_user.sgml index 68d37bbd86..44d52cd779 100644 --- a/doc/src/sgml/ref/create_user.sgml +++ b/doc/src/sgml/ref/create_user.sgml @@ -1,5 +1,5 @@ @@ -30,6 +30,7 @@ where option can be: | CREATEUSER | NOCREATEUSER | INHERIT | NOINHERIT | LOGIN | NOLOGIN + | CONNECTION LIMIT connlimit | [ ENCRYPTED | UNENCRYPTED ] PASSWORD 'password' | VALID UNTIL 'timestamp' | IN ROLE rolename [, ...] diff --git a/src/backend/access/transam/twophase.c b/src/backend/access/transam/twophase.c index 5f7ea98ffe..62ebf9fb40 100644 --- a/src/backend/access/transam/twophase.c +++ b/src/backend/access/transam/twophase.c @@ -7,7 +7,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/access/transam/twophase.c,v 1.8 2005/07/04 04:51:44 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/access/transam/twophase.c,v 1.9 2005/07/31 17:19:17 tgl Exp $ * * NOTES * Each global transaction is associated with a global transaction @@ -272,6 +272,7 @@ MarkAsPreparing(TransactionId xid, const char *gid, gxact->proc.xmin = InvalidTransactionId; gxact->proc.pid = 0; gxact->proc.databaseId = databaseid; + gxact->proc.roleId = owner; gxact->proc.lwWaiting = false; gxact->proc.lwExclusive = false; gxact->proc.lwWaitLink = NULL; diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql index 22e2c91110..f3f3b35680 100644 --- a/src/backend/catalog/system_views.sql +++ b/src/backend/catalog/system_views.sql @@ -3,7 +3,7 @@ * * Copyright (c) 1996-2005, PostgreSQL Global Development Group * - * $PostgreSQL: pgsql/src/backend/catalog/system_views.sql,v 1.17 2005/07/26 16:38:26 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/catalog/system_views.sql,v 1.18 2005/07/31 17:19:17 tgl Exp $ */ CREATE VIEW pg_roles AS @@ -15,6 +15,7 @@ CREATE VIEW pg_roles AS rolcreatedb, rolcatupdate, rolcanlogin, + rolconnlimit, '********'::text as rolpassword, rolvaliduntil, rolconfig, diff --git a/src/backend/commands/dbcommands.c b/src/backend/commands/dbcommands.c index 4e23def836..295ae955d1 100644 --- a/src/backend/commands/dbcommands.c +++ b/src/backend/commands/dbcommands.c @@ -15,7 +15,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/dbcommands.c,v 1.167 2005/07/14 21:46:29 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/commands/dbcommands.c,v 1.168 2005/07/31 17:19:17 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -92,10 +92,12 @@ createdb(const CreatedbStmt *stmt) DefElem *downer = NULL; DefElem *dtemplate = NULL; DefElem *dencoding = NULL; + DefElem *dconnlimit = NULL; char *dbname = stmt->dbname; char *dbowner = NULL; const char *dbtemplate = NULL; int encoding = -1; + int dbconnlimit = -1; #ifndef WIN32 char buf[2 * MAXPGPATH + 100]; @@ -141,6 +143,14 @@ createdb(const CreatedbStmt *stmt) errmsg("conflicting or redundant options"))); dencoding = defel; } + else if (strcmp(defel->defname, "connectionlimit") == 0) + { + if (dconnlimit) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("conflicting or redundant options"))); + dconnlimit = defel; + } else if (strcmp(defel->defname, "location") == 0) { ereport(WARNING, @@ -186,6 +196,8 @@ createdb(const CreatedbStmt *stmt) elog(ERROR, "unrecognized node type: %d", nodeTag(dencoding->arg)); } + if (dconnlimit && dconnlimit->arg) + dbconnlimit = intVal(dconnlimit->arg); /* obtain OID of proposed owner */ if (dbowner) @@ -484,6 +496,7 @@ createdb(const CreatedbStmt *stmt) new_record[Anum_pg_database_encoding - 1] = Int32GetDatum(encoding); new_record[Anum_pg_database_datistemplate - 1] = BoolGetDatum(false); new_record[Anum_pg_database_datallowconn - 1] = BoolGetDatum(true); + new_record[Anum_pg_database_datconnlimit - 1] = Int32GetDatum(dbconnlimit); new_record[Anum_pg_database_datlastsysoid - 1] = ObjectIdGetDatum(src_lastsysoid); new_record[Anum_pg_database_datvacuumxid - 1] = TransactionIdGetDatum(src_vacuumxid); new_record[Anum_pg_database_datfrozenxid - 1] = TransactionIdGetDatum(src_frozenxid); @@ -790,6 +803,98 @@ RenameDatabase(const char *oldname, const char *newname) } +/* + * ALTER DATABASE name ... + */ +void +AlterDatabase(AlterDatabaseStmt *stmt) +{ + Relation rel; + HeapTuple tuple, + newtuple; + ScanKeyData scankey; + SysScanDesc scan; + ListCell *option; + int connlimit = -1; + DefElem *dconnlimit = NULL; + Datum new_record[Natts_pg_database]; + char new_record_nulls[Natts_pg_database]; + char new_record_repl[Natts_pg_database]; + + /* Extract options from the statement node tree */ + foreach(option, stmt->options) + { + DefElem *defel = (DefElem *) lfirst(option); + + if (strcmp(defel->defname, "connectionlimit") == 0) + { + if (dconnlimit) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("conflicting or redundant options"))); + dconnlimit = defel; + } + else + elog(ERROR, "option \"%s\" not recognized", + defel->defname); + } + + if (dconnlimit) + connlimit = intVal(dconnlimit->arg); + + /* + * We don't need ExclusiveLock since we aren't updating the + * flat file. + */ + 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))); + + if (!pg_database_ownercheck(HeapTupleGetOid(tuple), GetUserId())) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_DATABASE, + stmt->dbname); + + /* + * Build an updated tuple, perusing the information just obtained + */ + MemSet(new_record, 0, sizeof(new_record)); + MemSet(new_record_nulls, ' ', sizeof(new_record_nulls)); + MemSet(new_record_repl, ' ', sizeof(new_record_repl)); + + if (dconnlimit) + { + new_record[Anum_pg_database_datconnlimit - 1] = Int32GetDatum(connlimit); + new_record_repl[Anum_pg_database_datconnlimit - 1] = 'r'; + } + + newtuple = heap_modifytuple(tuple, RelationGetDescr(rel), new_record, + new_record_nulls, new_record_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); + + /* + * We don't bother updating the flat file since the existing options + * for ALTER DATABASE don't affect it. + */ +} + + /* * ALTER DATABASE name SET ... */ diff --git a/src/backend/commands/user.c b/src/backend/commands/user.c index 6ef612dc4a..082ea0cf7a 100644 --- a/src/backend/commands/user.c +++ b/src/backend/commands/user.c @@ -6,7 +6,7 @@ * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/backend/commands/user.c,v 1.159 2005/07/26 22:37:49 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/commands/user.c,v 1.160 2005/07/31 17:19:17 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -86,6 +86,7 @@ CreateRole(CreateRoleStmt *stmt) bool createrole = false; /* Can this user create roles? */ bool createdb = false; /* Can the user create databases? */ bool canlogin = false; /* Can this user login? */ + int connlimit = -1; /* maximum connections allowed */ List *addroleto = NIL; /* roles to make this a member of */ List *rolemembers = NIL; /* roles to be members of this role */ List *adminmembers = NIL; /* roles to be admins of this role */ @@ -96,6 +97,7 @@ CreateRole(CreateRoleStmt *stmt) DefElem *dcreaterole = NULL; DefElem *dcreatedb = NULL; DefElem *dcanlogin = NULL; + DefElem *dconnlimit = NULL; DefElem *daddroleto = NULL; DefElem *drolemembers = NULL; DefElem *dadminmembers = NULL; @@ -178,6 +180,14 @@ CreateRole(CreateRoleStmt *stmt) errmsg("conflicting or redundant options"))); dcanlogin = defel; } + else if (strcmp(defel->defname, "connectionlimit") == 0) + { + if (dconnlimit) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("conflicting or redundant options"))); + dconnlimit = defel; + } else if (strcmp(defel->defname, "addroleto") == 0) { if (daddroleto) @@ -227,6 +237,8 @@ CreateRole(CreateRoleStmt *stmt) createdb = intVal(dcreatedb->arg) != 0; if (dcanlogin) canlogin = intVal(dcanlogin->arg) != 0; + if (dconnlimit) + connlimit = intVal(dconnlimit->arg); if (daddroleto) addroleto = (List *) daddroleto->arg; if (drolemembers) @@ -292,6 +304,7 @@ CreateRole(CreateRoleStmt *stmt) /* superuser gets catupdate right by default */ new_record[Anum_pg_authid_rolcatupdate - 1] = BoolGetDatum(issuper); new_record[Anum_pg_authid_rolcanlogin - 1] = BoolGetDatum(canlogin); + new_record[Anum_pg_authid_rolconnlimit - 1] = Int32GetDatum(connlimit); if (password) { @@ -401,6 +414,7 @@ AlterRole(AlterRoleStmt *stmt) int createrole = -1; /* Can this user create roles? */ int createdb = -1; /* Can the user create databases? */ int canlogin = -1; /* Can this user login? */ + int connlimit = -1; /* maximum connections allowed */ List *rolemembers = NIL; /* roles to be added/removed */ char *validUntil = NULL; /* time the login is valid until */ DefElem *dpassword = NULL; @@ -409,6 +423,7 @@ AlterRole(AlterRoleStmt *stmt) DefElem *dcreaterole = NULL; DefElem *dcreatedb = NULL; DefElem *dcanlogin = NULL; + DefElem *dconnlimit = NULL; DefElem *drolemembers = NULL; DefElem *dvalidUntil = NULL; Oid roleid; @@ -472,6 +487,14 @@ AlterRole(AlterRoleStmt *stmt) errmsg("conflicting or redundant options"))); dcanlogin = defel; } + else if (strcmp(defel->defname, "connectionlimit") == 0) + { + if (dconnlimit) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("conflicting or redundant options"))); + dconnlimit = defel; + } else if (strcmp(defel->defname, "rolemembers") == 0 && stmt->action != 0) { @@ -506,6 +529,8 @@ AlterRole(AlterRoleStmt *stmt) createdb = intVal(dcreatedb->arg); if (dcanlogin) canlogin = intVal(dcanlogin->arg); + if (dconnlimit) + connlimit = intVal(dconnlimit->arg); if (drolemembers) rolemembers = (List *) drolemembers->arg; if (dvalidUntil) @@ -545,6 +570,7 @@ AlterRole(AlterRoleStmt *stmt) createrole < 0 && createdb < 0 && canlogin < 0 && + !dconnlimit && !rolemembers && !validUntil && password && @@ -602,6 +628,12 @@ AlterRole(AlterRoleStmt *stmt) new_record_repl[Anum_pg_authid_rolcanlogin - 1] = 'r'; } + if (dconnlimit) + { + new_record[Anum_pg_authid_rolconnlimit - 1] = Int32GetDatum(connlimit); + new_record_repl[Anum_pg_authid_rolconnlimit - 1] = 'r'; + } + /* password */ if (password) { diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index 283cf54951..35fbee6164 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.312 2005/07/26 16:38:27 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.313 2005/07/31 17:19:18 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -2204,6 +2204,17 @@ _copyCreatedbStmt(CreatedbStmt *from) return newnode; } +static AlterDatabaseStmt * +_copyAlterDatabaseStmt(AlterDatabaseStmt *from) +{ + AlterDatabaseStmt *newnode = makeNode(AlterDatabaseStmt); + + COPY_STRING_FIELD(dbname); + COPY_NODE_FIELD(options); + + return newnode; +} + static AlterDatabaseSetStmt * _copyAlterDatabaseSetStmt(AlterDatabaseSetStmt *from) { @@ -3011,6 +3022,9 @@ copyObject(void *from) case T_CreatedbStmt: retval = _copyCreatedbStmt(from); break; + case T_AlterDatabaseStmt: + retval = _copyAlterDatabaseStmt(from); + break; case T_AlterDatabaseSetStmt: retval = _copyAlterDatabaseSetStmt(from); break; diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c index 47e989dbc7..6d727a6327 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -18,7 +18,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.249 2005/07/26 16:38:27 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.250 2005/07/31 17:19:18 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1151,6 +1151,15 @@ _equalCreatedbStmt(CreatedbStmt *a, CreatedbStmt *b) return true; } +static bool +_equalAlterDatabaseStmt(AlterDatabaseStmt *a, AlterDatabaseStmt *b) +{ + COMPARE_STRING_FIELD(dbname); + COMPARE_NODE_FIELD(options); + + return true; +} + static bool _equalAlterDatabaseSetStmt(AlterDatabaseSetStmt *a, AlterDatabaseSetStmt *b) { @@ -2059,6 +2068,9 @@ equal(void *a, void *b) case T_CreatedbStmt: retval = _equalCreatedbStmt(a, b); break; + case T_AlterDatabaseStmt: + retval = _equalAlterDatabaseStmt(a, b); + break; case T_AlterDatabaseSetStmt: retval = _equalAlterDatabaseSetStmt(a, b); break; diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 3ce9ec411b..f0475bf2be 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.504 2005/07/26 22:37:50 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.505 2005/07/31 17:19:18 tgl Exp $ * * HISTORY * AUTHOR DATE MAJOR EVENT @@ -131,9 +131,9 @@ static void doNegateFloat(Value *v); } %type stmt schema_stmt - AlterDatabaseSetStmt AlterDomainStmt AlterGroupStmt AlterOwnerStmt - AlterSeqStmt AlterTableStmt AlterUserStmt AlterUserSetStmt - AlterRoleStmt AlterRoleSetStmt + AlterDatabaseStmt AlterDatabaseSetStmt AlterDomainStmt AlterGroupStmt + AlterOwnerStmt AlterSeqStmt AlterTableStmt + AlterUserStmt AlterUserSetStmt AlterRoleStmt AlterRoleSetStmt AnalyzeStmt ClosePortalStmt ClusterStmt CommentStmt ConstraintsSetStmt CopyStmt CreateAsStmt CreateCastStmt CreateDomainStmt CreateGroupStmt CreateOpClassStmt CreatePLangStmt @@ -165,8 +165,10 @@ static void doNegateFloat(Value *v); %type opt_drop_behavior -%type createdb_opt_list copy_opt_list transaction_mode_list -%type createdb_opt_item copy_opt_item transaction_mode_item +%type createdb_opt_list alterdb_opt_list copy_opt_list + transaction_mode_list +%type createdb_opt_item alterdb_opt_item copy_opt_item + transaction_mode_item %type opt_lock lock_type cast_context %type opt_force opt_or_replace @@ -257,7 +259,7 @@ static void doNegateFloat(Value *v); %type copy_from opt_hold -%type fetch_count opt_column event cursor_options +%type opt_column event cursor_options %type reindex_type drop_type comment_type %type fetch_direction select_limit_value select_offset_value @@ -302,7 +304,7 @@ static void doNegateFloat(Value *v); %type opt_numeric opt_decimal %type opt_varying opt_timezone -%type Iconst +%type Iconst SignedIconst %type Sconst comment_text %type RoleId opt_granted_by opt_boolean ColId_or_Sconst %type var_list var_list_or_default @@ -342,7 +344,7 @@ static void doNegateFloat(Value *v); CACHE CALLED CASCADE CASE CAST CHAIN CHAR_P CHARACTER CHARACTERISTICS CHECK CHECKPOINT CLASS CLOSE CLUSTER COALESCE COLLATE COLUMN COMMENT COMMIT - COMMITTED CONSTRAINT CONSTRAINTS CONVERSION_P CONVERT COPY CREATE CREATEDB + COMMITTED CONNECTION CONSTRAINT CONSTRAINTS CONVERSION_P CONVERT COPY CREATE CREATEDB CREATEROLE CREATEUSER CROSS CSV CURRENT_DATE CURRENT_ROLE CURRENT_TIME CURRENT_TIMESTAMP CURRENT_USER CURSOR CYCLE @@ -486,7 +488,8 @@ stmtmulti: stmtmulti ';' stmt ; stmt : - AlterDatabaseSetStmt + AlterDatabaseStmt + | AlterDatabaseSetStmt | AlterDomainStmt | AlterFunctionStmt | AlterGroupStmt @@ -672,6 +675,10 @@ OptRoleElem: { $$ = makeDefElem("canlogin", (Node *)makeInteger(FALSE)); } + | CONNECTION LIMIT SignedIconst + { + $$ = makeDefElem("connectionlimit", (Node *)makeInteger($3)); + } | IN_P ROLE name_list { $$ = makeDefElem("addroleto", (Node *)$3); @@ -2238,17 +2245,8 @@ FloatOnly: FCONST { $$ = makeFloat($1); } } ; -IntegerOnly: - Iconst - { - $$ = makeInteger($1); - } - | '-' Iconst - { - $$ = makeInteger($2); - $$->val.ival = - $$->val.ival; - } - ; +IntegerOnly: SignedIconst { $$ = makeInteger($1); }; + /***************************************************************************** * @@ -3044,21 +3042,21 @@ fetch_direction: n->howMany = -1; $$ = (Node *)n; } - | ABSOLUTE_P fetch_count + | ABSOLUTE_P SignedIconst { FetchStmt *n = makeNode(FetchStmt); n->direction = FETCH_ABSOLUTE; n->howMany = $2; $$ = (Node *)n; } - | RELATIVE_P fetch_count + | RELATIVE_P SignedIconst { FetchStmt *n = makeNode(FetchStmt); n->direction = FETCH_RELATIVE; n->howMany = $2; $$ = (Node *)n; } - | fetch_count + | SignedIconst { FetchStmt *n = makeNode(FetchStmt); n->direction = FETCH_FORWARD; @@ -3079,7 +3077,7 @@ fetch_direction: n->howMany = 1; $$ = (Node *)n; } - | FORWARD fetch_count + | FORWARD SignedIconst { FetchStmt *n = makeNode(FetchStmt); n->direction = FETCH_FORWARD; @@ -3100,7 +3098,7 @@ fetch_direction: n->howMany = 1; $$ = (Node *)n; } - | BACKWARD fetch_count + | BACKWARD SignedIconst { FetchStmt *n = makeNode(FetchStmt); n->direction = FETCH_BACKWARD; @@ -3116,11 +3114,6 @@ fetch_direction: } ; -fetch_count: - Iconst { $$ = $1; } - | '-' Iconst { $$ = - $2; } - ; - from_in: FROM {} | IN_P {} ; @@ -4473,6 +4466,10 @@ createdb_opt_item: { $$ = makeDefElem("encoding", NULL); } + | CONNECTION LIMIT opt_equal SignedIconst + { + $$ = makeDefElem("connectionlimit", (Node *)makeInteger($4)); + } | OWNER opt_equal name { $$ = makeDefElem("owner", (Node *)makeString($3)); @@ -4485,8 +4482,7 @@ createdb_opt_item: /* * Though the equals sign doesn't match other WITH options, pg_dump uses - * equals for backward compability, and it doesn't seem worth removing it. - * 2002-02-25 + * equals for backward compatibility, and it doesn't seem worth removing it. */ opt_equal: '=' {} | /*EMPTY*/ {} @@ -4499,6 +4495,16 @@ opt_equal: '=' {} * *****************************************************************************/ +AlterDatabaseStmt: + ALTER DATABASE database_name opt_with alterdb_opt_list + { + AlterDatabaseStmt *n = makeNode(AlterDatabaseStmt); + n->dbname = $3; + n->options = $5; + $$ = (Node *)n; + } + ; + AlterDatabaseSetStmt: ALTER DATABASE database_name SET set_rest { @@ -4519,6 +4525,19 @@ AlterDatabaseSetStmt: ; +alterdb_opt_list: + alterdb_opt_list alterdb_opt_item { $$ = lappend($1, $2); } + | /* EMPTY */ { $$ = NIL; } + ; + +alterdb_opt_item: + CONNECTION LIMIT opt_equal SignedIconst + { + $$ = makeDefElem("connectionlimit", (Node *)makeInteger($4)); + } + ; + + /***************************************************************************** * * DROP DATABASE @@ -7875,6 +7894,10 @@ Iconst: ICONST { $$ = $1; }; Sconst: SCONST { $$ = $1; }; RoleId: ColId { $$ = $1; }; +SignedIconst: ICONST { $$ = $1; } + | '-' ICONST { $$ = - $2; } + ; + /* * Name classification hierarchy. * @@ -7959,6 +7982,7 @@ unreserved_keyword: | COMMENT | COMMIT | COMMITTED + | CONNECTION | CONSTRAINTS | CONVERSION_P | COPY diff --git a/src/backend/parser/keywords.c b/src/backend/parser/keywords.c index 5d4cab2124..9c09dc5bf3 100644 --- a/src/backend/parser/keywords.c +++ b/src/backend/parser/keywords.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/parser/keywords.c,v 1.163 2005/07/26 16:38:27 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/parser/keywords.c,v 1.164 2005/07/31 17:19:18 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -83,6 +83,7 @@ static const ScanKeyword ScanKeywords[] = { {"comment", COMMENT}, {"commit", COMMIT}, {"committed", COMMITTED}, + {"connection", CONNECTION}, {"constraint", CONSTRAINT}, {"constraints", CONSTRAINTS}, {"conversion", CONVERSION_P}, diff --git a/src/backend/storage/ipc/procarray.c b/src/backend/storage/ipc/procarray.c index 7c2766e628..1e2783dea8 100644 --- a/src/backend/storage/ipc/procarray.c +++ b/src/backend/storage/ipc/procarray.c @@ -23,7 +23,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/storage/ipc/procarray.c,v 1.3 2005/06/17 22:32:45 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/storage/ipc/procarray.c,v 1.4 2005/07/31 17:19:18 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -733,6 +733,60 @@ CountActiveBackends(void) return count; } +/* + * CountDBBackends --- count backends that are using specified database + */ +int +CountDBBackends(Oid databaseid) +{ + ProcArrayStruct *arrayP = procArray; + int count = 0; + int index; + + LWLockAcquire(ProcArrayLock, LW_SHARED); + + for (index = 0; index < arrayP->numProcs; index++) + { + PGPROC *proc = arrayP->procs[index]; + + if (proc->pid == 0) + continue; /* do not count prepared xacts */ + if (proc->databaseId == databaseid) + count++; + } + + LWLockRelease(ProcArrayLock); + + return count; +} + +/* + * CountUserBackends --- count backends that are used by specified user + */ +int +CountUserBackends(Oid roleid) +{ + ProcArrayStruct *arrayP = procArray; + int count = 0; + int index; + + LWLockAcquire(ProcArrayLock, LW_SHARED); + + for (index = 0; index < arrayP->numProcs; index++) + { + PGPROC *proc = arrayP->procs[index]; + + if (proc->pid == 0) + continue; /* do not count prepared xacts */ + if (proc->roleId == roleid) + count++; + } + + LWLockRelease(ProcArrayLock); + + return count; +} + #define XidCacheRemove(i) \ do { \ diff --git a/src/backend/storage/lmgr/proc.c b/src/backend/storage/lmgr/proc.c index 227c369478..11d0c70f6d 100644 --- a/src/backend/storage/lmgr/proc.c +++ b/src/backend/storage/lmgr/proc.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/storage/lmgr/proc.c,v 1.160 2005/06/17 22:32:45 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/storage/lmgr/proc.c,v 1.161 2005/07/31 17:19:19 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -254,6 +254,8 @@ InitProcess(void) MyProc->xmin = InvalidTransactionId; MyProc->pid = MyProcPid; MyProc->databaseId = MyDatabaseId; + /* Will be set properly after the session role id is determined */ + MyProc->roleId = InvalidOid; MyProc->lwWaiting = false; MyProc->lwExclusive = false; MyProc->lwWaitLink = NULL; @@ -331,6 +333,7 @@ InitDummyProcess(int proctype) MyProc->xid = InvalidTransactionId; MyProc->xmin = InvalidTransactionId; MyProc->databaseId = MyDatabaseId; + MyProc->roleId = InvalidOid; MyProc->lwWaiting = false; MyProc->lwExclusive = false; MyProc->lwWaitLink = NULL; diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c index 6df4546538..546aa49c2d 100644 --- a/src/backend/tcop/utility.c +++ b/src/backend/tcop/utility.c @@ -10,7 +10,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/tcop/utility.c,v 1.241 2005/07/14 05:13:41 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/tcop/utility.c,v 1.242 2005/07/31 17:19:19 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -275,6 +275,7 @@ check_xact_readonly(Node *parsetree) switch (nodeTag(parsetree)) { + case T_AlterDatabaseStmt: case T_AlterDatabaseSetStmt: case T_AlterDomainStmt: case T_AlterFunctionStmt: @@ -788,6 +789,10 @@ ProcessUtility(Node *parsetree, createdb((CreatedbStmt *) parsetree); break; + case T_AlterDatabaseStmt: + AlterDatabase((AlterDatabaseStmt *) parsetree); + break; + case T_AlterDatabaseSetStmt: AlterDatabaseSet((AlterDatabaseSetStmt *) parsetree); break; @@ -1504,6 +1509,10 @@ CreateCommandTag(Node *parsetree) tag = "CREATE DATABASE"; break; + case T_AlterDatabaseStmt: + tag = "ALTER DATABASE"; + break; + case T_AlterDatabaseSetStmt: tag = "ALTER DATABASE"; break; diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index 66d6d1725e..f6c50438e0 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.147 2005/07/25 22:12:33 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/utils/init/miscinit.c,v 1.148 2005/07/31 17:19:19 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -36,6 +36,8 @@ #include "storage/fd.h" #include "storage/ipc.h" #include "storage/pg_shmem.h" +#include "storage/proc.h" +#include "storage/procarray.h" #include "utils/builtins.h" #include "utils/guc.h" #include "utils/lsyscache.h" @@ -404,17 +406,52 @@ InitializeSessionUserId(const char *rolename) rform = (Form_pg_authid) GETSTRUCT(roleTup); roleid = HeapTupleGetOid(roleTup); - if (!rform->rolcanlogin) - ereport(FATAL, - (errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION), - errmsg("role \"%s\" is not permitted to log in", rolename))); - AuthenticatedUserId = roleid; AuthenticatedUserIsSuperuser = rform->rolsuper; /* This sets OuterUserId/CurrentUserId too */ SetSessionUserId(roleid, AuthenticatedUserIsSuperuser); + /* Also mark our PGPROC entry with the authenticated user id */ + /* (We assume this is an atomic store so no lock is needed) */ + MyProc->roleId = roleid; + + /* + * These next checks are not enforced when in standalone mode, so that + * there is a way to recover from sillinesses like + * "UPDATE pg_authid SET rolcanlogin = false;". + * + * We do not enforce them for the autovacuum process either. + */ + if (IsUnderPostmaster && !IsAutoVacuumProcess()) + { + /* + * Is role allowed to login at all? + */ + if (!rform->rolcanlogin) + ereport(FATAL, + (errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION), + errmsg("role \"%s\" is not permitted to log in", + rolename))); + /* + * Check connection limit for this role. + * + * There is a race condition here --- we create our PGPROC before + * checking for other PGPROCs. If two backends did this at about the + * same time, they might both think they were over the limit, while + * ideally one should succeed and one fail. Getting that to work + * exactly seems more trouble than it is worth, however; instead + * we just document that the connection limit is approximate. + */ + if (rform->rolconnlimit >= 0 && + !AuthenticatedUserIsSuperuser && + CountUserBackends(roleid) > rform->rolconnlimit) + ereport(FATAL, + (errcode(ERRCODE_TOO_MANY_CONNECTIONS), + errmsg("too many connections for role \"%s\"", + rolename))); + } + /* Record username and superuser status as GUC settings too */ SetConfigOption("session_authorization", rolename, PGC_BACKEND, PGC_S_OVERRIDE); diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c index b013eca86c..d06fa5db36 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.154 2005/07/29 19:30:05 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/utils/init/postinit.c,v 1.155 2005/07/31 17:19:19 tgl Exp $ * * *------------------------------------------------------------------------- @@ -169,18 +169,43 @@ ReverifyMyDatabase(const char *name) name, MyDatabaseId))); } - /* - * Also check that the database is currently allowing connections. - * (We do not enforce this in standalone mode, however, so that there is - * a way to recover from "UPDATE pg_database SET datallowconn = false;". - * We do not enforce it for the autovacuum process either.) - */ dbform = (Form_pg_database) GETSTRUCT(tup); - if (IsUnderPostmaster && !IsAutoVacuumProcess() && !dbform->datallowconn) - ereport(FATAL, - (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), - errmsg("database \"%s\" is not currently accepting connections", - name))); + + /* + * These next checks are not enforced when in standalone mode, so that + * there is a way to recover from disabling all access to all databases, + * for example "UPDATE pg_database SET datallowconn = false;". + * + * We do not enforce them for the autovacuum process either. + */ + if (IsUnderPostmaster && !IsAutoVacuumProcess()) + { + /* + * Check that the database is currently allowing connections. + */ + if (!dbform->datallowconn) + ereport(FATAL, + (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), + errmsg("database \"%s\" is not currently accepting connections", + name))); + /* + * Check connection limit for this database. + * + * There is a race condition here --- we create our PGPROC before + * checking for other PGPROCs. If two backends did this at about the + * same time, they might both think they were over the limit, while + * ideally one should succeed and one fail. Getting that to work + * exactly seems more trouble than it is worth, however; instead + * we just document that the connection limit is approximate. + */ + if (dbform->datconnlimit >= 0 && + !superuser() && + CountDBBackends(MyDatabaseId) > dbform->datconnlimit) + ereport(FATAL, + (errcode(ERRCODE_TOO_MANY_CONNECTIONS), + errmsg("too many connections for database \"%s\"", + name))); + } /* * OK, we're golden. Next to-do item is to save the encoding diff --git a/src/bin/pg_dump/pg_dumpall.c b/src/bin/pg_dump/pg_dumpall.c index 498c598e2b..28032c47b9 100644 --- a/src/bin/pg_dump/pg_dumpall.c +++ b/src/bin/pg_dump/pg_dumpall.c @@ -1,18 +1,19 @@ /*------------------------------------------------------------------------- * - * pg_dumpall + * pg_dumpall.c * * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * - * $PostgreSQL: pgsql/src/bin/pg_dump/pg_dumpall.c,v 1.65 2005/07/25 04:52:32 tgl Exp $ + * $PostgreSQL: pgsql/src/bin/pg_dump/pg_dumpall.c,v 1.66 2005/07/31 17:19:19 tgl Exp $ * *------------------------------------------------------------------------- */ #include "postgres_fe.h" +#include #include #ifdef ENABLE_NLS #include @@ -20,8 +21,6 @@ #ifndef HAVE_STRDUP #include "strdup.h" #endif -#include -#include #include "getopt_long.h" @@ -43,7 +42,8 @@ static const char *progname; static void help(void); -static void dumpUsers(PGconn *conn, bool initdbonly); +static void dumpRoles(PGconn *conn, bool initdbonly); +static void dumpRoleMembership(PGconn *conn); static void dumpGroups(PGconn *conn); static void dumpTablespaces(PGconn *conn); static void dumpCreateDB(PGconn *conn); @@ -57,19 +57,20 @@ static int runPgDump(const char *dbname); static PGconn *connectDatabase(const char *dbname, const char *pghost, const char *pgport, const char *pguser, bool require_password, bool fail_on_error); static PGresult *executeQuery(PGconn *conn, const char *query); +static void executeCommand(PGconn *conn, const char *query); -char pg_dump_bin[MAXPGPATH]; -PQExpBuffer pgdumpopts; -bool output_clean = false; -bool skip_acls = false; -bool verbose = false; -static bool ignoreVersion = false; -int server_version; - +static char pg_dump_bin[MAXPGPATH]; +static PQExpBuffer pgdumpopts; +static bool output_clean = false; +static bool skip_acls = false; +static bool verbose = false; +static bool ignoreVersion = false; /* flags for -X long options */ -int disable_dollar_quoting = 0; -int disable_triggers = 0; -int use_setsessauth = 0; +static int disable_dollar_quoting = 0; +static int disable_triggers = 0; +static int use_setsessauth = 0; +static int server_version; + int main(int argc, char *argv[]) @@ -316,14 +317,22 @@ main(int argc, char *argv[]) if (!data_only) { /* Dump all users excluding the initdb user */ - dumpUsers(conn, false); - dumpGroups(conn); + dumpRoles(conn, false); + + /* Dump role memberships --- need different method for pre-8.1 */ + if (server_version >= 80100) + dumpRoleMembership(conn); + else + dumpGroups(conn); + if (server_version >= 80000) dumpTablespaces(conn); + if (!globals_only) dumpCreateDB(conn); + /* Dump alter command for initdb user */ - dumpUsers(conn, true); + dumpRoles(conn, true); } if (!globals_only) @@ -384,81 +393,151 @@ help(void) /* - * Dump users + * Dump roles + * * Is able to dump all non initdb users or just the initdb user. */ static void -dumpUsers(PGconn *conn, bool initdbonly) +dumpRoles(PGconn *conn, bool initdbonly) { PQExpBuffer buf = createPQExpBuffer(); PGresult *res; + int i_rolname, + i_rolsuper, + i_rolinherit, + i_rolcreaterole, + i_rolcreatedb, + i_rolcatupdate, + i_rolcanlogin, + i_rolconnlimit, + i_rolpassword, + i_rolvaliduntil, + i_clusterowner; int i; - if (server_version >= 70100) - res = executeQuery(conn, - "SELECT usename, usesysid, passwd, usecreatedb, " - "usesuper, valuntil, " - "(usesysid = (SELECT datdba FROM pg_database WHERE datname = 'template0')) AS clusterowner " - "FROM pg_shadow"); + /* note: rolconfig is dumped later */ + if (server_version >= 80100) + printfPQExpBuffer(buf, + "SELECT rolname, rolsuper, rolinherit, " + "rolcreaterole, rolcreatedb, rolcatupdate, " + "rolcanlogin, rolconnlimit, rolpassword, " + "rolvaliduntil, " + "(oid = (SELECT datdba FROM pg_database WHERE datname = 'template0')) AS clusterowner " + "FROM pg_authid"); else - res = executeQuery(conn, - "SELECT usename, usesysid, passwd, usecreatedb, " - "usesuper, valuntil, " - "(usesysid = (SELECT datdba FROM pg_database WHERE datname = 'template1')) AS clusterowner " - "FROM pg_shadow"); + printfPQExpBuffer(buf, + "SELECT usename as rolname, " + "usesuper as rolsuper, " + "true as rolinherit, " + "usesuper as rolcreaterole, " + "usecreatedb as rolcreatedb, " + "usecatupd as rolcatupdate, " + "true as rolcanlogin, " + "-1 as rolconnlimit, " + "passwd as rolpassword, " + "valuntil as rolvaliduntil, " + "(usesysid = (SELECT datdba FROM pg_database WHERE datname = '%s')) AS clusterowner " + "FROM pg_shadow " + "UNION ALL " + "SELECT groname as rolname, " + "false as rolsuper, " + "true as rolinherit, " + "false as rolcreaterole, " + "false as rolcreatedb, " + "false as rolcatupdate, " + "false as rolcanlogin, " + "-1 as rolconnlimit, " + "null::text as rolpassword, " + "null::abstime as rolvaliduntil, " + "false AS clusterowner " + "FROM pg_group", + (server_version >= 70100) ? "template0" : "template1"); + + res = executeQuery(conn, buf->data); + + i_rolname = PQfnumber(res, "rolname"); + i_rolsuper = PQfnumber(res, "rolsuper"); + i_rolinherit = PQfnumber(res, "rolinherit"); + i_rolcreaterole = PQfnumber(res, "rolcreaterole"); + i_rolcreatedb = PQfnumber(res, "rolcreatedb"); + i_rolcatupdate = PQfnumber(res, "rolcatupdate"); + i_rolcanlogin = PQfnumber(res, "rolcanlogin"); + i_rolconnlimit = PQfnumber(res, "rolconnlimit"); + i_rolpassword = PQfnumber(res, "rolpassword"); + i_rolvaliduntil = PQfnumber(res, "rolvaliduntil"); + i_clusterowner = PQfnumber(res, "clusterowner"); if (PQntuples(res) > 0 || (!initdbonly && output_clean)) - printf("--\n-- Users\n--\n\n"); + printf("--\n-- Roles\n--\n\n"); if (!initdbonly && output_clean) printf("DELETE FROM pg_shadow WHERE usesysid <> (SELECT datdba FROM pg_database WHERE datname = 'template0');\n\n"); for (i = 0; i < PQntuples(res); i++) { - const char *username; + const char *rolename; bool clusterowner; - username = PQgetvalue(res, i, 0); - clusterowner = (strcmp(PQgetvalue(res, i, 6), "t") == 0); + rolename = PQgetvalue(res, i, i_rolname); + clusterowner = (strcmp(PQgetvalue(res, i, i_clusterowner), "t") == 0); /* Check which pass we're on */ if ((initdbonly && !clusterowner) || (!initdbonly && clusterowner)) continue; /* - * Dump ALTER USER for the cluster owner and CREATE USER for all - * other users + * Dump ALTER ROLE for the cluster owner and CREATE ROLE for all + * other roles */ if (!clusterowner) - printfPQExpBuffer(buf, "CREATE USER %s WITH", fmtId(username)); + printfPQExpBuffer(buf, "CREATE ROLE %s WITH", fmtId(rolename)); else - printfPQExpBuffer(buf, "ALTER USER %s WITH", fmtId(username)); + printfPQExpBuffer(buf, "ALTER ROLE %s WITH", fmtId(rolename)); - if (!PQgetisnull(res, i, 2)) - { - appendPQExpBuffer(buf, " PASSWORD "); - appendStringLiteral(buf, PQgetvalue(res, i, 2), true); - } + if (strcmp(PQgetvalue(res, i, i_rolsuper), "t") == 0) + appendPQExpBuffer(buf, " SUPERUSER"); + else + appendPQExpBuffer(buf, " NOSUPERUSER"); - if (strcmp(PQgetvalue(res, i, 3), "t") == 0) + if (strcmp(PQgetvalue(res, i, i_rolinherit), "t") == 0) + appendPQExpBuffer(buf, " INHERIT"); + else + appendPQExpBuffer(buf, " NOINHERIT"); + + if (strcmp(PQgetvalue(res, i, i_rolcreaterole), "t") == 0) + appendPQExpBuffer(buf, " CREATEROLE"); + else + appendPQExpBuffer(buf, " NOCREATEROLE"); + + if (strcmp(PQgetvalue(res, i, i_rolcreatedb), "t") == 0) appendPQExpBuffer(buf, " CREATEDB"); else appendPQExpBuffer(buf, " NOCREATEDB"); - if (strcmp(PQgetvalue(res, i, 4), "t") == 0) - appendPQExpBuffer(buf, " CREATEUSER"); + if (strcmp(PQgetvalue(res, i, i_rolcanlogin), "t") == 0) + appendPQExpBuffer(buf, " LOGIN"); else - appendPQExpBuffer(buf, " NOCREATEUSER"); + appendPQExpBuffer(buf, " NOLOGIN"); - if (!PQgetisnull(res, i, 5)) + if (strcmp(PQgetvalue(res, i, i_rolconnlimit), "-1") != 0) + appendPQExpBuffer(buf, " CONNECTION LIMIT %s", + PQgetvalue(res, i, i_rolconnlimit)); + + if (!PQgetisnull(res, i, i_rolpassword)) + { + appendPQExpBuffer(buf, " PASSWORD "); + appendStringLiteral(buf, PQgetvalue(res, i, i_rolpassword), true); + } + + if (!PQgetisnull(res, i, i_rolvaliduntil)) appendPQExpBuffer(buf, " VALID UNTIL '%s'", - PQgetvalue(res, i, 5)); + PQgetvalue(res, i, i_rolvaliduntil)); appendPQExpBuffer(buf, ";\n"); printf("%s", buf->data); if (server_version >= 70300) - dumpUserConfig(conn, username); + dumpUserConfig(conn, rolename); } PQclear(res); @@ -469,63 +548,109 @@ dumpUsers(PGconn *conn, bool initdbonly) } +/* + * Dump role memberships. This code is used for 8.1 and later servers. + * + * Note: we expect dumpRoles already created all the roles, but there is + * no membership yet. + */ +static void +dumpRoleMembership(PGconn *conn) +{ + PGresult *res; + int i; + + res = executeQuery(conn, "SELECT ur.rolname AS roleid, " + "um.rolname AS member, " + "ug.rolname AS grantor, " + "a.admin_option " + "FROM pg_auth_members a " + "LEFT JOIN pg_authid ur on ur.oid = a.roleid " + "LEFT JOIN pg_authid um on um.oid = a.member " + "LEFT JOIN pg_authid ug on ug.oid = a.grantor"); + + if (PQntuples(res) > 0 || output_clean) + printf("--\n-- Role memberships\n--\n\n"); + if (output_clean) + printf("DELETE FROM pg_auth_members;\n\n"); + + for (i = 0; i < PQntuples(res); i++) + { + char *roleid = PQgetvalue(res, i, 0); + char *member = PQgetvalue(res, i, 1); + char *grantor = PQgetvalue(res, i, 2); + char *option = PQgetvalue(res, i, 3); + + printf("GRANT %s", fmtId(roleid)); + printf(" TO %s", fmtId(member)); + if (*option == 't') + printf(" WITH ADMIN OPTION"); + printf(" GRANTED BY %s;\n", fmtId(grantor)); + } + + PQclear(res); + + printf("\n\n"); +} /* - * Dump groups. + * Dump group memberships from a pre-8.1 server. It's annoying that we + * can't share any useful amount of code with the post-8.1 case, but + * the catalog representations are too different. + * + * Note: we expect dumpRoles already created all the roles, but there is + * no membership yet. */ static void dumpGroups(PGconn *conn) { + PQExpBuffer buf = createPQExpBuffer(); PGresult *res; int i; res = executeQuery(conn, "SELECT groname, grolist FROM pg_group"); if (PQntuples(res) > 0 || output_clean) - printf("--\n-- Groups\n--\n\n"); + printf("--\n-- Role memberships\n--\n\n"); if (output_clean) - printf("DELETE FROM pg_group;\n\n"); + printf("DELETE FROM pg_auth_members;\n\n"); for (i = 0; i < PQntuples(res); i++) { - PQExpBuffer buf = createPQExpBuffer(); + char *groname = PQgetvalue(res, i, 0); char *val; char *tok; - appendPQExpBuffer(buf, "CREATE GROUP %s;\n", - fmtId(PQgetvalue(res, i, 0))); - val = strdup(PQgetvalue(res, i, 1)); + tok = strtok(val, ",{}"); while (tok) { PGresult *res2; - PQExpBuffer buf2 = createPQExpBuffer(); int j; - appendPQExpBuffer(buf2, "SELECT usename FROM pg_shadow WHERE usesysid = %s;", tok); - res2 = executeQuery(conn, buf2->data); - destroyPQExpBuffer(buf2); + printfPQExpBuffer(buf, + "SELECT usename FROM pg_shadow WHERE usesysid = %s", + tok); + + res2 = executeQuery(conn, buf->data); for (j = 0; j < PQntuples(res2); j++) { - appendPQExpBuffer(buf, "ALTER GROUP %s ", - fmtId(PQgetvalue(res, i, 0))); - appendPQExpBuffer(buf, "ADD USER %s;\n", - fmtId(PQgetvalue(res2, j, 0))); + printf("GRANT %s", fmtId(groname)); + printf(" TO %s;\n", fmtId(PQgetvalue(res2, j, 0))); } PQclear(res2); - tok = strtok(NULL, "{},"); + tok = strtok(NULL, ",{}"); } free(val); - - printf("%s", buf->data); - destroyPQExpBuffer(buf); } PQclear(res); + destroyPQExpBuffer(buf); + printf("\n\n"); } @@ -607,17 +732,27 @@ dumpTablespaces(PGconn *conn) static void dumpCreateDB(PGconn *conn) { + PQExpBuffer buf = createPQExpBuffer(); PGresult *res; int i; printf("--\n-- Database creation\n--\n\n"); - if (server_version >= 80000) + if (server_version >= 80100) + res = executeQuery(conn, + "SELECT datname, " + "coalesce(rolname, (select rolname from pg_authid where oid=(select datdba from pg_database where datname='template0'))), " + "pg_encoding_to_char(d.encoding), " + "datistemplate, datacl, datconnlimit, " + "(SELECT spcname FROM pg_tablespace t WHERE t.oid = d.dattablespace) AS dattablespace " + "FROM pg_database d LEFT JOIN pg_authid u ON (datdba = u.oid) " + "WHERE datallowconn ORDER BY 1"); + else if (server_version >= 80000) res = executeQuery(conn, "SELECT datname, " "coalesce(usename, (select usename from pg_shadow where usesysid=(select datdba from pg_database where datname='template0'))), " "pg_encoding_to_char(d.encoding), " - "datistemplate, datacl, " + "datistemplate, datacl, -1 as datconnlimit, " "(SELECT spcname FROM pg_tablespace t WHERE t.oid = d.dattablespace) AS dattablespace " "FROM pg_database d LEFT JOIN pg_shadow u ON (datdba = usesysid) " "WHERE datallowconn ORDER BY 1"); @@ -626,7 +761,7 @@ dumpCreateDB(PGconn *conn) "SELECT datname, " "coalesce(usename, (select usename from pg_shadow where usesysid=(select datdba from pg_database where datname='template0'))), " "pg_encoding_to_char(d.encoding), " - "datistemplate, datacl, " + "datistemplate, datacl, -1 as datconnlimit, " "'pg_default' AS dattablespace " "FROM pg_database d LEFT JOIN pg_shadow u ON (datdba = usesysid) " "WHERE datallowconn ORDER BY 1"); @@ -637,7 +772,7 @@ dumpCreateDB(PGconn *conn) "(select usename from pg_shadow where usesysid=datdba), " "(select usename from pg_shadow where usesysid=(select datdba from pg_database where datname='template0'))), " "pg_encoding_to_char(d.encoding), " - "datistemplate, '' as datacl, " + "datistemplate, '' as datacl, -1 as datconnlimit, " "'pg_default' AS dattablespace " "FROM pg_database d " "WHERE datallowconn ORDER BY 1"); @@ -652,7 +787,7 @@ dumpCreateDB(PGconn *conn) "(select usename from pg_shadow where usesysid=datdba), " "pg_encoding_to_char(d.encoding), " "'f' as datistemplate, " - "'' as datacl, " + "'' as datacl, -1 as datconnlimit, " "'pg_default' AS dattablespace " "FROM pg_database d " "ORDER BY 1"); @@ -660,18 +795,19 @@ dumpCreateDB(PGconn *conn) for (i = 0; i < PQntuples(res); i++) { - PQExpBuffer buf; char *dbname = PQgetvalue(res, i, 0); char *dbowner = PQgetvalue(res, i, 1); char *dbencoding = PQgetvalue(res, i, 2); char *dbistemplate = PQgetvalue(res, i, 3); char *dbacl = PQgetvalue(res, i, 4); - char *dbtablespace = PQgetvalue(res, i, 5); + char *dbconnlimit = PQgetvalue(res, i, 5); + char *dbtablespace = PQgetvalue(res, i, 6); char *fdbname; - buf = createPQExpBuffer(); fdbname = strdup(fmtId(dbname)); + resetPQExpBuffer(buf); + /* * Skip the CREATE DATABASE commands for "template1" and "postgres", * since they are presumably already there in the destination cluster. @@ -698,6 +834,10 @@ dumpCreateDB(PGconn *conn) appendPQExpBuffer(buf, " TABLESPACE = %s", fmtId(dbtablespace)); + if (strcmp(dbconnlimit, "-1") != 0) + appendPQExpBuffer(buf, " CONNECTION LIMIT = %s", + dbconnlimit); + appendPQExpBuffer(buf, ";\n"); if (strcmp(dbistemplate, "t") == 0) @@ -723,11 +863,12 @@ dumpCreateDB(PGconn *conn) if (server_version >= 70300) dumpDatabaseConfig(conn, dbname); - destroyPQExpBuffer(buf); free(fdbname); } PQclear(res); + destroyPQExpBuffer(buf); + printf("\n\n"); } @@ -782,14 +923,17 @@ dumpUserConfig(PGconn *conn, const char *username) { PGresult *res; - printfPQExpBuffer(buf, "SELECT useconfig[%d] FROM pg_shadow WHERE usename = ", count); + if (server_version >= 80100) + printfPQExpBuffer(buf, "SELECT rolconfig[%d] FROM pg_authid WHERE rolname = ", count); + else + printfPQExpBuffer(buf, "SELECT useconfig[%d] FROM pg_shadow WHERE usename = ", count); appendStringLiteral(buf, username, true); appendPQExpBuffer(buf, ";"); res = executeQuery(conn, buf->data); if (!PQgetisnull(res, 0, 0)) { - makeAlterConfigCommand(PQgetvalue(res, 0, 0), "USER", username); + makeAlterConfigCommand(PQgetvalue(res, 0, 0), "ROLE", username); PQclear(res); count++; } @@ -1041,11 +1185,17 @@ connectDatabase(const char *dbname, const char *pghost, const char *pgport, } } + /* + * On 7.3 and later, make sure we are not fooled by non-system schemas + * in the search path. + */ + if (server_version >= 70300) + executeCommand(conn, "SET search_path = pg_catalog"); + return conn; } - /* * Run a query, return the results, exit program on failure. */ @@ -1061,8 +1211,10 @@ executeQuery(PGconn *conn, const char *query) if (!res || PQresultStatus(res) != PGRES_TUPLES_OK) { - fprintf(stderr, _("%s: query failed: %s"), progname, PQerrorMessage(conn)); - fprintf(stderr, _("%s: query was: %s\n"), progname, query); + fprintf(stderr, _("%s: query failed: %s"), + progname, PQerrorMessage(conn)); + fprintf(stderr, _("%s: query was: %s\n"), + progname, query); PQfinish(conn); exit(1); } @@ -1070,6 +1222,32 @@ executeQuery(PGconn *conn, const char *query) return res; } +/* + * As above for a SQL command (which returns nothing). + */ +static void +executeCommand(PGconn *conn, const char *query) +{ + PGresult *res; + + if (verbose) + fprintf(stderr, _("%s: executing %s\n"), progname, query); + + res = PQexec(conn, query); + if (!res || + PQresultStatus(res) != PGRES_COMMAND_OK) + { + fprintf(stderr, _("%s: query failed: %s"), + progname, PQerrorMessage(conn)); + fprintf(stderr, _("%s: query was: %s\n"), + progname, query); + PQfinish(conn); + exit(1); + } + + PQclear(res); +} + /* * dumpTimestamp diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index ec2813be89..c7584968a3 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -37,7 +37,7 @@ * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.293 2005/07/29 15:04:22 momjian Exp $ + * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.294 2005/07/31 17:19:20 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -53,6 +53,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 200507291 +#define CATALOG_VERSION_NO 200507301 #endif diff --git a/src/include/catalog/pg_authid.h b/src/include/catalog/pg_authid.h index 6672138d86..bcdee017d1 100644 --- a/src/include/catalog/pg_authid.h +++ b/src/include/catalog/pg_authid.h @@ -10,7 +10,7 @@ * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/catalog/pg_authid.h,v 1.2 2005/07/26 16:38:28 tgl Exp $ + * $PostgreSQL: pgsql/src/include/catalog/pg_authid.h,v 1.3 2005/07/31 17:19:21 tgl Exp $ * * NOTES * the genbki.sh script reads this file and generates .bki @@ -49,6 +49,7 @@ CATALOG(pg_authid,1260) BKI_SHARED_RELATION bool rolcreatedb; /* allowed to create databases? */ bool rolcatupdate; /* allowed to alter catalogs manually? */ bool rolcanlogin; /* allowed to log in as session user? */ + int4 rolconnlimit; /* max connections allowed (-1=no limit) */ /* remaining fields may be null; use heap_getattr to read them! */ text rolpassword; /* password, if any */ @@ -70,7 +71,7 @@ typedef FormData_pg_authid *Form_pg_authid; * compiler constants for pg_authid * ---------------- */ -#define Natts_pg_authid 10 +#define Natts_pg_authid 11 #define Anum_pg_authid_rolname 1 #define Anum_pg_authid_rolsuper 2 #define Anum_pg_authid_rolinherit 3 @@ -78,9 +79,10 @@ typedef FormData_pg_authid *Form_pg_authid; #define Anum_pg_authid_rolcreatedb 5 #define Anum_pg_authid_rolcatupdate 6 #define Anum_pg_authid_rolcanlogin 7 -#define Anum_pg_authid_rolpassword 8 -#define Anum_pg_authid_rolvaliduntil 9 -#define Anum_pg_authid_rolconfig 10 +#define Anum_pg_authid_rolconnlimit 8 +#define Anum_pg_authid_rolpassword 9 +#define Anum_pg_authid_rolvaliduntil 10 +#define Anum_pg_authid_rolconfig 11 /* ---------------- * initial contents of pg_authid @@ -89,7 +91,7 @@ typedef FormData_pg_authid *Form_pg_authid; * user choices. * ---------------- */ -DATA(insert OID = 10 ( "POSTGRES" t t t t t t _null_ _null_ _null_ )); +DATA(insert OID = 10 ( "POSTGRES" t t t t t t -1 _null_ _null_ _null_ )); #define BOOTSTRAP_SUPERUSERID 10 diff --git a/src/include/catalog/pg_database.h b/src/include/catalog/pg_database.h index 37c57f8508..5ad5737825 100644 --- a/src/include/catalog/pg_database.h +++ b/src/include/catalog/pg_database.h @@ -8,7 +8,7 @@ * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/catalog/pg_database.h,v 1.36 2005/06/28 05:09:06 tgl Exp $ + * $PostgreSQL: pgsql/src/include/catalog/pg_database.h,v 1.37 2005/07/31 17:19:21 tgl Exp $ * * NOTES * the genbki.sh script reads this file and generates .bki @@ -40,6 +40,7 @@ CATALOG(pg_database,1262) BKI_SHARED_RELATION int4 encoding; /* character encoding */ bool datistemplate; /* allowed as CREATE DATABASE template? */ bool datallowconn; /* new connections allowed? */ + int4 datconnlimit; /* max connections allowed (-1=no limit) */ Oid datlastsysoid; /* highest OID to consider a system OID */ TransactionId datvacuumxid; /* all XIDs before this are vacuumed */ TransactionId datfrozenxid; /* all XIDs before this are frozen */ @@ -59,20 +60,21 @@ typedef FormData_pg_database *Form_pg_database; * compiler constants for pg_database * ---------------- */ -#define Natts_pg_database 11 +#define Natts_pg_database 12 #define Anum_pg_database_datname 1 #define Anum_pg_database_datdba 2 #define Anum_pg_database_encoding 3 #define Anum_pg_database_datistemplate 4 #define Anum_pg_database_datallowconn 5 -#define Anum_pg_database_datlastsysoid 6 -#define Anum_pg_database_datvacuumxid 7 -#define Anum_pg_database_datfrozenxid 8 -#define Anum_pg_database_dattablespace 9 -#define Anum_pg_database_datconfig 10 -#define Anum_pg_database_datacl 11 +#define Anum_pg_database_datconnlimit 6 +#define Anum_pg_database_datlastsysoid 7 +#define Anum_pg_database_datvacuumxid 8 +#define Anum_pg_database_datfrozenxid 9 +#define Anum_pg_database_dattablespace 10 +#define Anum_pg_database_datconfig 11 +#define Anum_pg_database_datacl 12 -DATA(insert OID = 1 ( template1 PGUID ENCODING t t 0 0 0 1663 _null_ _null_ )); +DATA(insert OID = 1 ( template1 PGUID ENCODING t t -1 0 0 0 1663 _null_ _null_ )); DESCR("Default template database"); #define TemplateDbOid 1 diff --git a/src/include/commands/dbcommands.h b/src/include/commands/dbcommands.h index 8020fc7e4e..7770ea9b97 100644 --- a/src/include/commands/dbcommands.h +++ b/src/include/commands/dbcommands.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/commands/dbcommands.h,v 1.40 2005/07/08 04:12:27 neilc Exp $ + * $PostgreSQL: pgsql/src/include/commands/dbcommands.h,v 1.41 2005/07/31 17:19:21 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -55,6 +55,7 @@ typedef struct xl_dbase_drop_rec extern void createdb(const CreatedbStmt *stmt); extern void dropdb(const char *dbname); extern void RenameDatabase(const char *oldname, const char *newname); +extern void AlterDatabase(AlterDatabaseStmt *stmt); extern void AlterDatabaseSet(AlterDatabaseSetStmt *stmt); extern void AlterDatabaseOwner(const char *dbname, Oid newOwnerId); diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h index 3e623911c7..a488d8dce7 100644 --- a/src/include/nodes/nodes.h +++ b/src/include/nodes/nodes.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/nodes/nodes.h,v 1.172 2005/06/28 05:09:13 tgl Exp $ + * $PostgreSQL: pgsql/src/include/nodes/nodes.h,v 1.173 2005/07/31 17:19:21 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -270,6 +270,7 @@ typedef enum NodeTag T_ReindexStmt, T_CheckPointStmt, T_CreateSchemaStmt, + T_AlterDatabaseStmt, T_AlterDatabaseSetStmt, T_AlterRoleSetStmt, T_CreateConversionStmt, diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 6d388b07d3..68262d28f7 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.286 2005/07/26 16:38:28 tgl Exp $ + * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.287 2005/07/31 17:19:21 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1624,6 +1624,13 @@ typedef struct CreatedbStmt * Alter Database * ---------------------- */ +typedef struct AlterDatabaseStmt +{ + NodeTag type; + char *dbname; /* name of database to alter */ + List *options; /* List of DefElem nodes */ +} AlterDatabaseStmt; + typedef struct AlterDatabaseSetStmt { NodeTag type; diff --git a/src/include/storage/proc.h b/src/include/storage/proc.h index ece321028f..63090de1cf 100644 --- a/src/include/storage/proc.h +++ b/src/include/storage/proc.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/storage/proc.h,v 1.79 2005/06/17 22:32:50 tgl Exp $ + * $PostgreSQL: pgsql/src/include/storage/proc.h,v 1.80 2005/07/31 17:19:22 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -71,6 +71,7 @@ struct PGPROC int pid; /* This backend's process id, or 0 */ Oid databaseId; /* OID of database this backend is using */ + Oid roleId; /* OID of role using this backend */ /* Info about LWLock the process is currently waiting for, if any. */ bool lwWaiting; /* true if waiting for an LW lock */ diff --git a/src/include/storage/procarray.h b/src/include/storage/procarray.h index d1780bcca1..aa0adfec88 100644 --- a/src/include/storage/procarray.h +++ b/src/include/storage/procarray.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/storage/procarray.h,v 1.2 2005/06/17 22:32:50 tgl Exp $ + * $PostgreSQL: pgsql/src/include/storage/procarray.h,v 1.3 2005/07/31 17:19:22 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -31,6 +31,8 @@ extern bool IsBackendPid(int pid); extern bool DatabaseHasActiveBackends(Oid databaseId, bool ignoreMyself); extern int CountActiveBackends(void); +extern int CountDBBackends(Oid databaseid); +extern int CountUserBackends(Oid roleid); extern void XidCacheRemoveRunningXids(TransactionId xid, int nxids, TransactionId *xids); diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out index e204864c3c..55b0653a3b 100644 --- a/src/test/regress/expected/rules.out +++ b/src/test/regress/expected/rules.out @@ -1281,7 +1281,7 @@ SELECT viewname, definition FROM pg_views WHERE schemaname <> 'information_schem pg_indexes | SELECT n.nspname AS schemaname, c.relname AS tablename, i.relname AS indexname, t.spcname AS "tablespace", pg_get_indexdef(i.oid) AS indexdef FROM ((((pg_index x JOIN pg_class c ON ((c.oid = x.indrelid))) JOIN pg_class i ON ((i.oid = x.indexrelid))) LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) LEFT JOIN pg_tablespace t ON ((t.oid = i.reltablespace))) WHERE ((c.relkind = 'r'::"char") AND (i.relkind = 'i'::"char")); pg_locks | SELECT l.locktype, l."database", l.relation, l.page, l.tuple, l.transactionid, l.classid, l.objid, l.objsubid, l."transaction", l.pid, l."mode", l."granted" FROM pg_lock_status() l(locktype text, "database" oid, relation oid, page integer, tuple smallint, transactionid xid, classid oid, objid oid, objsubid smallint, "transaction" xid, pid integer, "mode" text, "granted" boolean); pg_prepared_xacts | SELECT p."transaction", p.gid, p."prepared", u.rolname AS "owner", d.datname AS "database" FROM ((pg_prepared_xact() p("transaction" xid, gid text, "prepared" timestamp with time zone, ownerid oid, dbid oid) LEFT JOIN pg_authid u ON ((p.ownerid = u.oid))) LEFT JOIN pg_database d ON ((p.dbid = d.oid))); - pg_roles | SELECT pg_authid.rolname, pg_authid.rolsuper, pg_authid.rolinherit, pg_authid.rolcreaterole, pg_authid.rolcreatedb, pg_authid.rolcatupdate, pg_authid.rolcanlogin, '********'::text AS rolpassword, pg_authid.rolvaliduntil, pg_authid.rolconfig, pg_authid.oid FROM pg_authid; + pg_roles | SELECT pg_authid.rolname, pg_authid.rolsuper, pg_authid.rolinherit, pg_authid.rolcreaterole, pg_authid.rolcreatedb, pg_authid.rolcatupdate, pg_authid.rolcanlogin, pg_authid.rolconnlimit, '********'::text AS rolpassword, pg_authid.rolvaliduntil, pg_authid.rolconfig, pg_authid.oid FROM pg_authid; pg_rules | SELECT n.nspname AS schemaname, c.relname AS tablename, r.rulename, pg_get_ruledef(r.oid) AS definition FROM ((pg_rewrite r JOIN pg_class c ON ((c.oid = r.ev_class))) LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE (r.rulename <> '_RETURN'::name); pg_settings | SELECT a.name, a.setting, a.category, a.short_desc, a.extra_desc, a.context, a.vartype, a.source, a.min_val, a.max_val FROM pg_show_all_settings() a(name text, setting text, category text, short_desc text, extra_desc text, context text, vartype text, source text, min_val text, max_val text); pg_shadow | SELECT pg_authid.rolname AS usename, pg_authid.oid AS usesysid, pg_authid.rolcreatedb AS usecreatedb, pg_authid.rolsuper AS usesuper, pg_authid.rolcatupdate AS usecatupd, pg_authid.rolpassword AS passwd, (pg_authid.rolvaliduntil)::abstime AS valuntil, pg_authid.rolconfig AS useconfig FROM pg_authid WHERE pg_authid.rolcanlogin;