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:
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 ...
|
||||
*/
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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},
|
||||
|
||||
@@ -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 { \
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user