Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Lane2005-07-31 17:19:22 +0000
committerTom Lane2005-07-31 17:19:22 +0000
commitd42cf5a42a42689f68bc1ee1200aca75f954b1fd (patch)
treeedce5e6641f56c2c7e836e52a8b6586d1ab45cb5 /src/backend
parentb12587710701f63b4d8bac49f59901f210edf6b6 (diff)
Add per-user and per-database connection limit options.
This patch also includes preliminary update of pg_dumpall for roles. Petr Jelinek, with review by Bruce Momjian and Tom Lane.
Diffstat (limited to 'src/backend')
-rw-r--r--src/backend/access/transam/twophase.c3
-rw-r--r--src/backend/catalog/system_views.sql3
-rw-r--r--src/backend/commands/dbcommands.c107
-rw-r--r--src/backend/commands/user.c34
-rw-r--r--src/backend/nodes/copyfuncs.c16
-rw-r--r--src/backend/nodes/equalfuncs.c14
-rw-r--r--src/backend/parser/gram.y90
-rw-r--r--src/backend/parser/keywords.c3
-rw-r--r--src/backend/storage/ipc/procarray.c56
-rw-r--r--src/backend/storage/lmgr/proc.c5
-rw-r--r--src/backend/tcop/utility.c11
-rw-r--r--src/backend/utils/init/miscinit.c49
-rw-r--r--src/backend/utils/init/postinit.c47
13 files changed, 378 insertions, 60 deletions
diff --git a/src/backend/access/transam/twophase.c b/src/backend/access/transam/twophase.c
index 5f7ea98ffe1..62ebf9fb406 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 22e2c911107..f3f3b356808 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 4e23def8361..295ae955d1f 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);
@@ -791,6 +804,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 ...
*/
void
diff --git a/src/backend/commands/user.c b/src/backend/commands/user.c
index 6ef612dc4a9..082ea0cf7a0 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 283cf549514..35fbee61641 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 47e989dbc7d..6d727a63278 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 $
*
*-------------------------------------------------------------------------
*/
@@ -1152,6 +1152,15 @@ _equalCreatedbStmt(CreatedbStmt *a, CreatedbStmt *b)
}
static bool
+_equalAlterDatabaseStmt(AlterDatabaseStmt *a, AlterDatabaseStmt *b)
+{
+ COMPARE_STRING_FIELD(dbname);
+ COMPARE_NODE_FIELD(options);
+
+ return true;
+}
+
+static bool
_equalAlterDatabaseSetStmt(AlterDatabaseSetStmt *a, AlterDatabaseSetStmt *b)
{
COMPARE_STRING_FIELD(dbname);
@@ -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 3ce9ec411b7..f0475bf2bee 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 <node> 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 <dbehavior> opt_drop_behavior
-%type <list> createdb_opt_list copy_opt_list transaction_mode_list
-%type <defelt> createdb_opt_item copy_opt_item transaction_mode_item
+%type <list> createdb_opt_list alterdb_opt_list copy_opt_list
+ transaction_mode_list
+%type <defelt> createdb_opt_item alterdb_opt_item copy_opt_item
+ transaction_mode_item
%type <ival> opt_lock lock_type cast_context
%type <boolean> opt_force opt_or_replace
@@ -257,7 +259,7 @@ static void doNegateFloat(Value *v);
%type <boolean> copy_from opt_hold
-%type <ival> fetch_count opt_column event cursor_options
+%type <ival> opt_column event cursor_options
%type <objtype> reindex_type drop_type comment_type
%type <node> fetch_direction select_limit_value select_offset_value
@@ -302,7 +304,7 @@ static void doNegateFloat(Value *v);
%type <ival> opt_numeric opt_decimal
%type <boolean> opt_varying opt_timezone
-%type <ival> Iconst
+%type <ival> Iconst SignedIconst
%type <str> Sconst comment_text
%type <str> RoleId opt_granted_by opt_boolean ColId_or_Sconst
%type <list> 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 5d4cab2124f..9c09dc5bf3b 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 7c2766e6285..1e2783dea80 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 227c3694788..11d0c70f6df 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 6df4546538d..546aa49c2d0 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 66d6d1725e0..f6c50438e03 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 b013eca86cf..d06fa5db369 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)));
}
+ dbform = (Form_pg_database) GETSTRUCT(tup);
+
/*
- * 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.)
+ * 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.
*/
- 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)));
+ 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