1
0
mirror of https://github.com/postgres/postgres.git synced 2025-11-10 17:42:29 +03:00

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.
This commit is contained in:
Tom Lane
2005-07-31 17:19:22 +00:00
parent b125877107
commit d42cf5a42a
30 changed files with 783 additions and 180 deletions

View File

@@ -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;

View File

@@ -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,

View File

@@ -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 ...
*/

View File

@@ -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)
{

View File

@@ -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;

View File

@@ -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;

View File

@@ -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

View File

@@ -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},

View File

@@ -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 { \

View File

@@ -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;

View File

@@ -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;

View File

@@ -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);

View File

@@ -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