Index: src/backend/commands/dbcommands.c =================================================================== RCS file: /projects/cvsroot/pgsql/src/backend/commands/dbcommands.c,v retrieving revision 1.164 diff -c -r1.164 dbcommands.c *** src/backend/commands/dbcommands.c 30 Jun 2005 00:00:50 -0000 1.164 --- src/backend/commands/dbcommands.c 3 Jul 2005 22:47:39 -0000 *************** *** 53,60 **** /* non-export function prototypes */ static bool get_db_info(const char *name, Oid *dbIdP, Oid *ownerIdP, ! int *encodingP, bool *dbIsTemplateP, bool *dbAllowConnP, ! Oid *dbLastSysOidP, TransactionId *dbVacuumXidP, TransactionId *dbFrozenXidP, Oid *dbTablespace); static bool have_createdb_privilege(void); --- 53,60 ---- /* non-export function prototypes */ static bool get_db_info(const char *name, Oid *dbIdP, Oid *ownerIdP, ! int *encodingP, int *dbMaxConnP, bool *dbIsTemplateP, ! bool *dbAllowConnP, Oid *dbLastSysOidP, TransactionId *dbVacuumXidP, TransactionId *dbFrozenXidP, Oid *dbTablespace); static bool have_createdb_privilege(void); *************** *** 74,79 **** --- 74,80 ---- int src_encoding; bool src_istemplate; bool src_allowconn; + int src_maxconn; Oid src_lastsysoid; TransactionId src_vacuumxid; TransactionId src_frozenxid; *************** *** 91,100 **** --- 92,103 ---- DefElem *downer = NULL; DefElem *dtemplate = NULL; DefElem *dencoding = NULL; + DefElem *dmaxconn = NULL; char *dbname = stmt->dbname; char *dbowner = NULL; const char *dbtemplate = NULL; int encoding = -1; + int dbmaxconn = -1; #ifndef WIN32 char buf[2 * MAXPGPATH + 100]; *************** *** 140,145 **** --- 143,156 ---- errmsg("conflicting or redundant options"))); dencoding = defel; } + else if (strcmp(defel->defname, "maxconnections") == 0) + { + if (dmaxconn) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("conflicting or redundant options"))); + dmaxconn = defel; + } else if (strcmp(defel->defname, "location") == 0) { ereport(WARNING, *************** *** 185,190 **** --- 196,203 ---- elog(ERROR, "unrecognized node type: %d", nodeTag(dencoding->arg)); } + if (dmaxconn && dmaxconn->arg) + dbmaxconn = intVal(dmaxconn->arg); /* obtain OID of proposed owner */ if (dbowner) *************** *** 218,224 **** * idea, so accept possibility of race to create. We will check again * after we grab the exclusive lock. */ ! if (get_db_info(dbname, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL)) ereport(ERROR, (errcode(ERRCODE_DUPLICATE_DATABASE), --- 231,237 ---- * idea, so accept possibility of race to create. We will check again * after we grab the exclusive lock. */ ! if (get_db_info(dbname, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL)) ereport(ERROR, (errcode(ERRCODE_DUPLICATE_DATABASE), *************** *** 231,238 **** dbtemplate = "template1"; /* Default template database name */ if (!get_db_info(dbtemplate, &src_dboid, &src_owner, &src_encoding, ! &src_istemplate, &src_allowconn, &src_lastsysoid, ! &src_vacuumxid, &src_frozenxid, &src_deftablespace)) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_DATABASE), errmsg("template database \"%s\" does not exist", dbtemplate))); --- 244,252 ---- dbtemplate = "template1"; /* Default template database name */ if (!get_db_info(dbtemplate, &src_dboid, &src_owner, &src_encoding, ! &src_maxconn, &src_istemplate, &src_allowconn, ! &src_lastsysoid, &src_vacuumxid, &src_frozenxid, ! &src_deftablespace)) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_DATABASE), errmsg("template database \"%s\" does not exist", dbtemplate))); *************** *** 266,271 **** --- 280,289 ---- if (encoding < 0) encoding = src_encoding; + /* If dbmaxconn is defaulted, use source's dbmaxconn */ + if (dbmaxconn < 0) + dbmaxconn = src_maxconn; + /* Some encodings are client only */ if (!PG_VALID_BE_ENCODING(encoding)) ereport(ERROR, *************** *** 461,467 **** pg_database_rel = heap_open(DatabaseRelationId, ExclusiveLock); /* Check to see if someone else created same DB name meanwhile. */ ! if (get_db_info(dbname, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL)) { /* Don't hold lock while doing recursive remove */ --- 479,485 ---- pg_database_rel = heap_open(DatabaseRelationId, ExclusiveLock); /* Check to see if someone else created same DB name meanwhile. */ ! if (get_db_info(dbname, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL)) { /* Don't hold lock while doing recursive remove */ *************** *** 487,492 **** --- 505,511 ---- 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_datmaxconn - 1] = Int32GetDatum(dbmaxconn); 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); *************** *** 587,593 **** */ pgdbrel = heap_open(DatabaseRelationId, ExclusiveLock); ! if (!get_db_info(dbname, &db_id, NULL, NULL, &db_istemplate, NULL, NULL, NULL, NULL, NULL)) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_DATABASE), --- 606,612 ---- */ pgdbrel = heap_open(DatabaseRelationId, ExclusiveLock); ! if (!get_db_info(dbname, &db_id, NULL, NULL, NULL, &db_istemplate, NULL, NULL, NULL, NULL, NULL)) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_DATABASE), *************** *** 783,788 **** --- 802,892 ---- /* + * ALTER DATABASE name ... + */ + void + AlterDatabase(AlterDatabaseStmt *stmt) + { + Datum new_record[Natts_pg_database]; + char new_record_nulls[Natts_pg_database]; + char new_record_repl[Natts_pg_database]; + Relation rel; + HeapTuple tuple, + newtuple; + ScanKeyData scankey; + SysScanDesc scan; + ListCell *option; + int maxconn = -1; /* Maximum connections allowed */ + + DefElem *dmaxconn = NULL; + + /* Extract options from the statement node tree */ + foreach(option, stmt->options) + { + DefElem *defel = (DefElem *) lfirst(option); + + if (strcmp(defel->defname, "maxconnections") == 0) + { + if (dmaxconn) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("conflicting or redundant options"))); + dmaxconn = defel; + } + } + + if (dmaxconn) + maxconn = intVal(dmaxconn->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 (!have_createdb_privilege()) + 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 (maxconn >= 0) + { + new_record[Anum_pg_database_datmaxconn - 1] = Int32GetDatum(maxconn); + new_record_repl[Anum_pg_database_datmaxconn - 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); + } + + + /* * ALTER DATABASE name SET ... */ void *************** *** 971,978 **** static bool get_db_info(const char *name, Oid *dbIdP, Oid *ownerIdP, ! int *encodingP, bool *dbIsTemplateP, bool *dbAllowConnP, ! Oid *dbLastSysOidP, TransactionId *dbVacuumXidP, TransactionId *dbFrozenXidP, Oid *dbTablespace) { --- 1075,1082 ---- static bool get_db_info(const char *name, Oid *dbIdP, Oid *ownerIdP, ! int *encodingP, int *dbMaxConnP, bool *dbIsTemplateP, ! bool *dbAllowConnP, Oid *dbLastSysOidP, TransactionId *dbVacuumXidP, TransactionId *dbFrozenXidP, Oid *dbTablespace) { *************** *** 1017,1022 **** --- 1121,1129 ---- /* allowing connections? */ if (dbAllowConnP) *dbAllowConnP = dbform->datallowconn; + /* maximum connections */ + if (dbMaxConnP) + *dbMaxConnP = dbform->datmaxconn; /* last system OID used in database */ if (dbLastSysOidP) *dbLastSysOidP = dbform->datlastsysoid; Index: src/backend/commands/user.c =================================================================== RCS file: /projects/cvsroot/pgsql/src/backend/commands/user.c,v retrieving revision 1.155 diff -c -r1.155 user.c *** src/backend/commands/user.c 29 Jun 2005 20:34:13 -0000 1.155 --- src/backend/commands/user.c 3 Jul 2005 22:47:54 -0000 *************** *** 85,90 **** --- 85,91 ---- bool createrole = false; /* Can this user create roles? */ bool createdb = false; /* Can the user create databases? */ bool canlogin = false; /* Can this user login? */ + int maxconn = 0; /* 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 */ *************** *** 94,99 **** --- 95,101 ---- DefElem *dcreaterole = NULL; DefElem *dcreatedb = NULL; DefElem *dcanlogin = NULL; + DefElem *dmaxconn = NULL; DefElem *daddroleto = NULL; DefElem *drolemembers = NULL; DefElem *dadminmembers = NULL; *************** *** 155,160 **** --- 157,170 ---- errmsg("conflicting or redundant options"))); dcanlogin = defel; } + else if (strcmp(defel->defname, "maxconnections") == 0) + { + if (dmaxconn) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("conflicting or redundant options"))); + dmaxconn = defel; + } else if (strcmp(defel->defname, "addroleto") == 0) { if (daddroleto) *************** *** 202,207 **** --- 212,230 ---- createdb = intVal(dcreatedb->arg) != 0; if (dcanlogin) canlogin = intVal(dcanlogin->arg) != 0; + if (dmaxconn) + { + maxconn = intVal(dmaxconn->arg); + if (maxconn < 0) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("MAX CONNECTIONS must not be negative"))); + + if (!canlogin && maxconn > 0) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("MAX CONNECTIONS can be specified only for roles which can login"))); + } if (daddroleto) addroleto = (List *) daddroleto->arg; if (drolemembers) *************** *** 265,270 **** --- 288,294 ---- /* 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_rolmaxconn - 1] = Int32GetDatum(maxconn); if (password) { *************** *** 369,374 **** --- 393,399 ---- int createrole = -1; /* Can this user create roles? */ int createdb = -1; /* Can the user create databases? */ int canlogin = -1; /* Can this user login? */ + int maxconn = -1; /* maximum connections allowed */ List *rolemembers = NIL; /* roles to be added/removed */ char *validUntil = NULL; /* time the login is valid until */ DefElem *dpassword = NULL; *************** *** 376,381 **** --- 401,407 ---- DefElem *dcreaterole = NULL; DefElem *dcreatedb = NULL; DefElem *dcanlogin = NULL; + DefElem *dmaxconn = NULL; DefElem *drolemembers = NULL; DefElem *dvalidUntil = NULL; Oid roleid; *************** *** 431,436 **** --- 457,470 ---- errmsg("conflicting or redundant options"))); dcanlogin = defel; } + else if (strcmp(defel->defname, "maxconnections") == 0) + { + if (dmaxconn) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("conflicting or redundant options"))); + dmaxconn = defel; + } else if (strcmp(defel->defname, "rolemembers") == 0 && stmt->action != 0) { *************** *** 463,468 **** --- 497,515 ---- createdb = intVal(dcreatedb->arg); if (dcanlogin) canlogin = intVal(dcanlogin->arg); + if (dmaxconn) + { + maxconn = intVal(dmaxconn->arg); + if (maxconn < 0) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("MAX CONNECTIONS must not be negative"))); + + if (canlogin == 0 && maxconn > 0) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("MAX CONNECTIONS can be specified only for roles which can login"))); + } if (drolemembers) rolemembers = (List *) drolemembers->arg; if (dvalidUntil) *************** *** 502,507 **** --- 549,555 ---- !(createrole < 0 && createdb < 0 && canlogin < 0 && + maxconn < 0 && !rolemembers && !validUntil && password && *************** *** 553,558 **** --- 601,612 ---- new_record_repl[Anum_pg_authid_rolcanlogin - 1] = 'r'; } + if (maxconn >= 0) + { + new_record[Anum_pg_authid_rolmaxconn - 1] = Int32GetDatum(maxconn); + new_record_repl[Anum_pg_authid_rolmaxconn - 1] = 'r'; + } + /* password */ if (password) { Index: src/backend/libpq/crypt.c =================================================================== RCS file: /projects/cvsroot/pgsql/src/backend/libpq/crypt.c,v retrieving revision 1.64 diff -c -r1.64 crypt.c *** src/backend/libpq/crypt.c 29 Jun 2005 22:51:54 -0000 1.64 --- src/backend/libpq/crypt.c 3 Jul 2005 22:47:57 -0000 *************** *** 42,52 **** if ((line = get_role_line(role)) == NULL) return STATUS_ERROR; ! /* Skip over rolename */ token = list_head(*line); if (token) token = lnext(token); if (token) { shadow_pass = (char *) lfirst(token); token = lnext(token); --- 42,54 ---- if ((line = get_role_line(role)) == NULL) return STATUS_ERROR; ! /* Skip over rolename and roleid */ token = list_head(*line); if (token) token = lnext(token); if (token) + token = lnext(token); + if (token) { shadow_pass = (char *) lfirst(token); token = lnext(token); Index: src/backend/libpq/hba.c =================================================================== RCS file: /projects/cvsroot/pgsql/src/backend/libpq/hba.c,v retrieving revision 1.144 diff -c -r1.144 hba.c *** src/backend/libpq/hba.c 28 Jun 2005 22:16:45 -0000 1.144 --- src/backend/libpq/hba.c 3 Jul 2005 22:48:12 -0000 *************** *** 494,505 **** return true; /* ! * skip over the role name, password, valuntil, examine all the * membership entries */ ! if (list_length(*line) < 4) return false; ! for_each_cell(line_item, lnext(lnext(lnext(list_head(*line))))) { if (strcmp((char *) lfirst(line_item), role) == 0) return true; --- 494,505 ---- return true; /* ! * skip over the role name, id, password, valuntil, examine all the * membership entries */ ! if (list_length(*line) < 5) return false; ! for_each_cell(line_item, lnext(lnext(lnext(lnext(list_head(*line)))))) { if (strcmp((char *) lfirst(line_item), role) == 0) return true; Index: src/backend/nodes/copyfuncs.c =================================================================== RCS file: /projects/cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v retrieving revision 1.311 diff -c -r1.311 copyfuncs.c *** src/backend/nodes/copyfuncs.c 2 Jul 2005 23:00:39 -0000 1.311 --- src/backend/nodes/copyfuncs.c 3 Jul 2005 22:48:36 -0000 *************** *** 2204,2209 **** --- 2204,2220 ---- 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) { *************** *** 3010,3015 **** --- 3021,3029 ---- case T_CreatedbStmt: retval = _copyCreatedbStmt(from); break; + case T_AlterDatabaseStmt: + retval = _copyAlterDatabaseStmt(from); + break; case T_AlterDatabaseSetStmt: retval = _copyAlterDatabaseSetStmt(from); break; Index: src/backend/nodes/equalfuncs.c =================================================================== RCS file: /projects/cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v retrieving revision 1.248 diff -c -r1.248 equalfuncs.c *** src/backend/nodes/equalfuncs.c 2 Jul 2005 23:00:39 -0000 1.248 --- src/backend/nodes/equalfuncs.c 3 Jul 2005 22:48:53 -0000 *************** *** 1152,1157 **** --- 1152,1166 ---- } 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); *************** *** 2058,2063 **** --- 2067,2075 ---- case T_CreatedbStmt: retval = _equalCreatedbStmt(a, b); break; + case T_AlterDatabaseStmt: + retval = _equalAlterDatabaseStmt(a, b); + break; case T_AlterDatabaseSetStmt: retval = _equalAlterDatabaseSetStmt(a, b); break; Index: src/backend/parser/gram.y =================================================================== RCS file: /projects/cvsroot/pgsql/src/backend/parser/gram.y,v retrieving revision 2.501 diff -c -r2.501 gram.y *** src/backend/parser/gram.y 29 Jun 2005 20:34:13 -0000 2.501 --- src/backend/parser/gram.y 3 Jul 2005 22:50:20 -0000 *************** *** 131,139 **** } %type stmt schema_stmt ! AlterDatabaseSetStmt AlterDomainStmt AlterGroupStmt AlterOwnerStmt ! AlterSeqStmt AlterTableStmt AlterUserStmt AlterUserSetStmt ! AlterRoleStmt AlterRoleSetStmt AnalyzeStmt ClosePortalStmt ClusterStmt CommentStmt ConstraintsSetStmt CopyStmt CreateAsStmt CreateCastStmt CreateDomainStmt CreateGroupStmt CreateOpClassStmt CreatePLangStmt --- 131,139 ---- } %type stmt schema_stmt ! AlterDatabaseStmt AlterDatabaseSetStmt AlterDomainStmt AlterGroupStmt ! AlterOwnerStmt AlterSeqStmt AlterTableStmt ! AlterUserStmt AlterUserSetStmt AlterRoleStmt AlterRoleSetStmt AnalyzeStmt ClosePortalStmt ClusterStmt CommentStmt ConstraintsSetStmt CopyStmt CreateAsStmt CreateCastStmt CreateDomainStmt CreateGroupStmt CreateOpClassStmt CreatePLangStmt *************** *** 165,172 **** %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 opt_lock lock_type cast_context %type opt_force opt_or_replace --- 165,174 ---- %type opt_drop_behavior ! %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 *************** *** 342,348 **** 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 CREATEROLE CREATEUSER CROSS CSV CURRENT_DATE CURRENT_ROLE CURRENT_TIME CURRENT_TIMESTAMP CURRENT_USER CURSOR CYCLE --- 344,350 ---- CACHE CALLED CASCADE CASE CAST CHAIN CHAR_P CHARACTER CHARACTERISTICS CHECK CHECKPOINT CLASS CLOSE CLUSTER COALESCE COLLATE COLUMN COMMENT COMMIT ! COMMITTED CONNECTIONS CONSTRAINT CONSTRAINTS CONVERSION_P CONVERT COPY CREATE CREATEDB CREATEROLE CREATEUSER CROSS CSV CURRENT_DATE CURRENT_ROLE CURRENT_TIME CURRENT_TIMESTAMP CURRENT_USER CURSOR CYCLE *************** *** 373,379 **** LIKE LIMIT LISTEN LOAD LOCAL LOCALTIME LOCALTIMESTAMP LOCATION LOCK_P LOGIN_P ! MATCH MAXVALUE MINUTE_P MINVALUE MODE MONTH_P MOVE NAMES NATIONAL NATURAL NCHAR NEW NEXT NO NOCREATEDB NOCREATEROLE NOCREATEUSER NOLOGIN_P NONE NOSUPERUSER NOT NOTHING NOTIFY --- 375,381 ---- LIKE LIMIT LISTEN LOAD LOCAL LOCALTIME LOCALTIMESTAMP LOCATION LOCK_P LOGIN_P ! MATCH MAX MAXVALUE MINUTE_P MINVALUE MODE MONTH_P MOVE NAMES NATIONAL NATURAL NCHAR NEW NEXT NO NOCREATEDB NOCREATEROLE NOCREATEUSER NOLOGIN_P NONE NOSUPERUSER NOT NOTHING NOTIFY *************** *** 486,492 **** ; stmt : ! AlterDatabaseSetStmt | AlterDomainStmt | AlterFunctionStmt | AlterGroupStmt --- 488,495 ---- ; stmt : ! AlterDatabaseStmt ! | AlterDatabaseSetStmt | AlterDomainStmt | AlterFunctionStmt | AlterGroupStmt *************** *** 663,668 **** --- 666,675 ---- { $$ = makeDefElem("canlogin", (Node *)makeInteger(FALSE)); } + | MAX CONNECTIONS Iconst + { + $$ = makeDefElem("maxconnections", (Node *)makeInteger($3)); + } | IN_P ROLE name_list { $$ = makeDefElem("addroleto", (Node *)$3); *************** *** 4455,4460 **** --- 4462,4471 ---- { $$ = makeDefElem("encoding", NULL); } + | MAX CONNECTIONS opt_equal Iconst + { + $$ = makeDefElem("maxconnections", (Node *)makeInteger($4)); + } | OWNER opt_equal name { $$ = makeDefElem("owner", (Node *)makeString($3)); *************** *** 4481,4486 **** --- 4492,4507 ---- * *****************************************************************************/ + 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 { *************** *** 4501,4506 **** --- 4522,4540 ---- ; + alterdb_opt_list: + alterdb_opt_list alterdb_opt_item { $$ = lappend($1, $2); } + | /* EMPTY */ { $$ = NIL; } + ; + + alterdb_opt_item: + MAX CONNECTIONS opt_equal Iconst + { + $$ = makeDefElem("maxconnections", (Node *)makeInteger($4)); + } + ; + + /***************************************************************************** * * DROP DATABASE *************** *** 7941,7946 **** --- 7975,7981 ---- | COMMENT | COMMIT | COMMITTED + | CONNECTIONS | CONSTRAINTS | CONVERSION_P | COPY *************** *** 8009,8014 **** --- 8044,8050 ---- | LOCK_P | LOGIN_P | MATCH + | MAX | MAXVALUE | MINUTE_P | MINVALUE Index: src/backend/parser/keywords.c =================================================================== RCS file: /projects/cvsroot/pgsql/src/backend/parser/keywords.c,v retrieving revision 1.162 diff -c -r1.162 keywords.c *** src/backend/parser/keywords.c 29 Jun 2005 20:34:14 -0000 1.162 --- src/backend/parser/keywords.c 3 Jul 2005 22:50:24 -0000 *************** *** 83,88 **** --- 83,89 ---- {"comment", COMMENT}, {"commit", COMMIT}, {"committed", COMMITTED}, + {"connections", CONNECTIONS}, {"constraint", CONSTRAINT}, {"constraints", CONSTRAINTS}, {"conversion", CONVERSION_P}, *************** *** 203,208 **** --- 204,210 ---- {"lock", LOCK_P}, {"login", LOGIN_P}, {"match", MATCH}, + {"max", MAX}, {"maxvalue", MAXVALUE}, {"minute", MINUTE_P}, {"minvalue", MINVALUE}, Index: src/backend/storage/ipc/procarray.c =================================================================== RCS file: /projects/cvsroot/pgsql/src/backend/storage/ipc/procarray.c,v retrieving revision 1.3 diff -c -r1.3 procarray.c *** src/backend/storage/ipc/procarray.c 17 Jun 2005 22:32:45 -0000 1.3 --- src/backend/storage/ipc/procarray.c 3 Jul 2005 22:50:36 -0000 *************** *** 734,739 **** --- 734,790 ---- } + /* + * 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 && 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 && proc->roleId == roleid) + count++; + } + + LWLockRelease(ProcArrayLock); + + return count; + } + + #define XidCacheRemove(i) \ do { \ MyProc->subxids.xids[i] = MyProc->subxids.xids[MyProc->subxids.nxids - 1]; \ Index: src/backend/storage/lmgr/proc.c =================================================================== RCS file: /projects/cvsroot/pgsql/src/backend/storage/lmgr/proc.c,v retrieving revision 1.160 diff -c -r1.160 proc.c *** src/backend/storage/lmgr/proc.c 17 Jun 2005 22:32:45 -0000 1.160 --- src/backend/storage/lmgr/proc.c 3 Jul 2005 22:50:51 -0000 *************** *** 254,259 **** --- 254,260 ---- MyProc->xmin = InvalidTransactionId; MyProc->pid = MyProcPid; MyProc->databaseId = MyDatabaseId; + MyProc->roleId = GetSessionUserId(); MyProc->lwWaiting = false; MyProc->lwExclusive = false; MyProc->lwWaitLink = NULL; Index: src/backend/tcop/utility.c =================================================================== RCS file: /projects/cvsroot/pgsql/src/backend/tcop/utility.c,v retrieving revision 1.240 diff -c -r1.240 utility.c *** src/backend/tcop/utility.c 30 Jun 2005 00:00:51 -0000 1.240 --- src/backend/tcop/utility.c 3 Jul 2005 22:51:06 -0000 *************** *** 275,280 **** --- 275,281 ---- switch (nodeTag(parsetree)) { + case T_AlterDatabaseStmt: case T_AlterDatabaseSetStmt: case T_AlterDomainStmt: case T_AlterFunctionStmt: *************** *** 788,793 **** --- 789,798 ---- createdb((CreatedbStmt *) parsetree); break; + case T_AlterDatabaseStmt: + AlterDatabase((AlterDatabaseStmt *) parsetree); + break; + case T_AlterDatabaseSetStmt: AlterDatabaseSet((AlterDatabaseSetStmt *) parsetree); break; *************** *** 1504,1509 **** --- 1509,1518 ---- tag = "CREATE DATABASE"; break; + case T_AlterDatabaseStmt: + tag = "ALTER DATABASE"; + break; + case T_AlterDatabaseSetStmt: tag = "ALTER DATABASE"; break; Index: src/backend/utils/init/flatfiles.c =================================================================== RCS file: /projects/cvsroot/pgsql/src/backend/utils/init/flatfiles.c,v retrieving revision 1.11 diff -c -r1.11 flatfiles.c *** src/backend/utils/init/flatfiles.c 29 Jun 2005 20:34:15 -0000 1.11 --- src/backend/utils/init/flatfiles.c 3 Jul 2005 22:51:18 -0000 *************** *** 629,635 **** ListCell *mem; fputs_quote(arole->rolname, fp); ! fputs(" ", fp); fputs_quote(arole->rolpassword, fp); fputs(" ", fp); fputs_quote(arole->rolvaliduntil, fp); --- 629,635 ---- ListCell *mem; fputs_quote(arole->rolname, fp); ! fprintf(fp, " %u ", arole->roleid); fputs_quote(arole->rolpassword, fp); fputs(" ", fp); fputs_quote(arole->rolvaliduntil, fp); Index: src/backend/utils/init/miscinit.c =================================================================== RCS file: /projects/cvsroot/pgsql/src/backend/utils/init/miscinit.c,v retrieving revision 1.144 diff -c -r1.144 miscinit.c *** src/backend/utils/init/miscinit.c 28 Jun 2005 22:16:45 -0000 1.144 --- src/backend/utils/init/miscinit.c 3 Jul 2005 22:51:29 -0000 *************** *** 39,44 **** --- 39,45 ---- #include "utils/guc.h" #include "utils/lsyscache.h" #include "utils/syscache.h" + #include "storage/procarray.h" ProcessingMode Mode = InitProcessing; *************** *** 347,352 **** --- 348,365 ---- SetSessionUserId(roleid); /* sets CurrentUserId too */ + /* + * Check connection limit for user + */ + if (rform->rolmaxconn > 0 && !AuthenticatedUserIsSuperuser && + CountUserBackends(AuthenticatedUserId) > rform->rolmaxconn) + { + ereport(FATAL, + (errcode(ERRCODE_TOO_MANY_CONNECTIONS), + errmsg("sorry, too many clients already for role \"%s\"", + rolename))); + } + /* Record username and superuser status as GUC settings too */ SetConfigOption("session_authorization", rolename, PGC_BACKEND, PGC_S_OVERRIDE); Index: src/backend/utils/init/postinit.c =================================================================== RCS file: /projects/cvsroot/pgsql/src/backend/utils/init/postinit.c,v retrieving revision 1.151 diff -c -r1.151 postinit.c *** src/backend/utils/init/postinit.c 28 Jun 2005 19:51:23 -0000 1.151 --- src/backend/utils/init/postinit.c 3 Jul 2005 22:51:37 -0000 *************** *** 47,52 **** --- 47,53 ---- static bool FindMyDatabase(const char *name, Oid *db_id, Oid *db_tablespace); + static bool FindMyRole(const char *name, Oid *role_id); static void ReverifyMyDatabase(const char *name); static void InitCommunication(void); static void ShutdownPostgres(int code, Datum arg); *************** *** 101,106 **** --- 102,136 ---- } /* + * Get roleid from flatfiles + * + * We need this because we need to know userid before + * InitProcess() is called + */ + static bool + FindMyRole(const char *name, Oid *role_id) + { + List **line; + ListCell *token; + + if ((line = get_role_line(name)) == NULL) + ereport(FATAL, + (ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION, + errmsg("could not find role \"%s\"", name))); + + token = list_head(*line); + if (token) + token = lnext(token); + if (token) + { + *role_id = atoi((char*)lfirst(token)); + return true; + } + + return false; + } + + /* * ReverifyMyDatabase -- recheck info obtained by FindMyDatabase * * Since FindMyDatabase cannot lock pg_database, the information it read *************** *** 166,182 **** 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;") - */ dbform = (Form_pg_database) GETSTRUCT(tup); ! if (IsUnderPostmaster && !dbform->datallowconn) ! ereport(FATAL, ! (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), ! errmsg("database \"%s\" is not currently accepting connections", name))); /* * OK, we're golden. Next to-do item is to save the encoding --- 196,230 ---- name, MyDatabaseId))); } dbform = (Form_pg_database) GETSTRUCT(tup); ! if (IsUnderPostmaster) ! { ! /* ! * 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;") ! */ ! if (!dbform->datallowconn) ! { ! ereport(FATAL, ! (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), ! errmsg("database \"%s\" is not currently accepting connections", ! name))); ! } ! ! /* ! * Here we check cxonenction limit for this database ! */ ! if (dbform->datmaxconn > 0 && !superuser() && ! CountDBBackends(MyDatabaseId) > dbform->datmaxconn) ! { ! ereport(FATAL, ! (errcode(ERRCODE_TOO_MANY_CONNECTIONS), ! errmsg("sorry, too many clients already for database \"%s\"", name))); + } + } + /* * OK, we're golden. Next to-do item is to save the encoding *************** *** 352,357 **** --- 400,424 ---- */ /* + * We need to know roleid in InitProcess() so we have read it from + * flatfile, real user inicialization is done later + */ + if (IsUnderPostmaster) + { + Oid roleid; + + if (!FindMyRole(username, &roleid)) + ereport(FATAL, + (ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION, + errmsg("role \"%s\" does not exist", + username))); + + SetSessionUserId(roleid); + } + else + SetSessionUserId(BOOTSTRAP_SUPERUSERID); + + /* * Set up my per-backend PGPROC struct in shared memory. (We need * to know MyDatabaseId before we can do this, since it's entered into * the PGPROC struct.) Index: src/include/catalog/pg_authid.h =================================================================== RCS file: /projects/cvsroot/pgsql/src/include/catalog/pg_authid.h,v retrieving revision 1.1 diff -c -r1.1 pg_authid.h *** src/include/catalog/pg_authid.h 28 Jun 2005 05:09:05 -0000 1.1 --- src/include/catalog/pg_authid.h 3 Jul 2005 22:51:50 -0000 *************** *** 48,53 **** --- 48,54 ---- bool rolcreatedb; /* allowed to create databases? */ bool rolcatupdate; /* allowed to alter catalogs manually? */ bool rolcanlogin; /* allowed to log in as session user? */ + int4 rolmaxconn; /* maximum connections allowed */ /* remaining fields may be null; use heap_getattr to read them! */ text rolpassword; /* password, if any */ *************** *** 69,84 **** * compiler constants for pg_authid * ---------------- */ ! #define Natts_pg_authid 9 #define Anum_pg_authid_rolname 1 #define Anum_pg_authid_rolsuper 2 #define Anum_pg_authid_rolcreaterole 3 #define Anum_pg_authid_rolcreatedb 4 #define Anum_pg_authid_rolcatupdate 5 #define Anum_pg_authid_rolcanlogin 6 ! #define Anum_pg_authid_rolpassword 7 ! #define Anum_pg_authid_rolvaliduntil 8 ! #define Anum_pg_authid_rolconfig 9 /* ---------------- * initial contents of pg_authid --- 70,86 ---- * compiler constants for pg_authid * ---------------- */ ! #define Natts_pg_authid 10 #define Anum_pg_authid_rolname 1 #define Anum_pg_authid_rolsuper 2 #define Anum_pg_authid_rolcreaterole 3 #define Anum_pg_authid_rolcreatedb 4 #define Anum_pg_authid_rolcatupdate 5 #define Anum_pg_authid_rolcanlogin 6 ! #define Anum_pg_authid_rolmaxconn 7 ! #define Anum_pg_authid_rolpassword 8 ! #define Anum_pg_authid_rolvaliduntil 9 ! #define Anum_pg_authid_rolconfig 10 /* ---------------- * initial contents of pg_authid *************** *** 87,93 **** * user choices. * ---------------- */ ! DATA(insert OID = 10 ( "POSTGRES" t t t t t _null_ _null_ _null_ )); #define BOOTSTRAP_SUPERUSERID 10 --- 89,95 ---- * user choices. * ---------------- */ ! DATA(insert OID = 10 ( "POSTGRES" t t t t t 0 _null_ _null_ _null_ )); #define BOOTSTRAP_SUPERUSERID 10 Index: src/include/catalog/pg_database.h =================================================================== RCS file: /projects/cvsroot/pgsql/src/include/catalog/pg_database.h,v retrieving revision 1.36 diff -c -r1.36 pg_database.h *** src/include/catalog/pg_database.h 28 Jun 2005 05:09:06 -0000 1.36 --- src/include/catalog/pg_database.h 3 Jul 2005 22:51:51 -0000 *************** *** 40,45 **** --- 40,46 ---- int4 encoding; /* character encoding */ bool datistemplate; /* allowed as CREATE DATABASE template? */ bool datallowconn; /* new connections allowed? */ + int4 datmaxconn; /* maximum connections allowed */ 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,78 **** * compiler constants for pg_database * ---------------- */ ! #define Natts_pg_database 11 #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 ! DATA(insert OID = 1 ( template1 PGUID ENCODING t t 0 0 0 1663 _null_ _null_ )); DESCR("Default template database"); #define TemplateDbOid 1 --- 60,80 ---- * compiler constants for pg_database * ---------------- */ ! #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_datmaxconn 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 0 1663 _null_ _null_ )); DESCR("Default template database"); #define TemplateDbOid 1 Index: src/include/commands/dbcommands.h =================================================================== RCS file: /projects/cvsroot/pgsql/src/include/commands/dbcommands.h,v retrieving revision 1.39 diff -c -r1.39 dbcommands.h *** src/include/commands/dbcommands.h 28 Jun 2005 05:09:12 -0000 1.39 --- src/include/commands/dbcommands.h 3 Jul 2005 22:51:53 -0000 *************** *** 64,69 **** --- 64,70 ---- 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); Index: src/include/nodes/nodes.h =================================================================== RCS file: /projects/cvsroot/pgsql/src/include/nodes/nodes.h,v retrieving revision 1.172 diff -c -r1.172 nodes.h *** src/include/nodes/nodes.h 28 Jun 2005 05:09:13 -0000 1.172 --- src/include/nodes/nodes.h 3 Jul 2005 22:52:00 -0000 *************** *** 270,275 **** --- 270,276 ---- T_ReindexStmt, T_CheckPointStmt, T_CreateSchemaStmt, + T_AlterDatabaseStmt, T_AlterDatabaseSetStmt, T_AlterRoleSetStmt, T_CreateConversionStmt, Index: src/include/nodes/parsenodes.h =================================================================== RCS file: /projects/cvsroot/pgsql/src/include/nodes/parsenodes.h,v retrieving revision 1.285 diff -c -r1.285 parsenodes.h *** src/include/nodes/parsenodes.h 28 Jun 2005 19:51:24 -0000 1.285 --- src/include/nodes/parsenodes.h 3 Jul 2005 22:52:25 -0000 *************** *** 1611,1616 **** --- 1611,1623 ---- * 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; Index: src/include/storage/proc.h =================================================================== RCS file: /projects/cvsroot/pgsql/src/include/storage/proc.h,v retrieving revision 1.79 diff -c -r1.79 proc.h *** src/include/storage/proc.h 17 Jun 2005 22:32:50 -0000 1.79 --- src/include/storage/proc.h 3 Jul 2005 22:52:29 -0000 *************** *** 71,76 **** --- 71,77 ---- int pid; /* This backend's process id, or 0 */ Oid databaseId; /* OID of database this backend is using */ + Oid roleId; /* OID of role using conencted to backend */ /* Info about LWLock the process is currently waiting for, if any. */ bool lwWaiting; /* true if waiting for an LW lock */ Index: src/include/storage/procarray.h =================================================================== RCS file: /projects/cvsroot/pgsql/src/include/storage/procarray.h,v retrieving revision 1.2 diff -c -r1.2 procarray.h *** src/include/storage/procarray.h 17 Jun 2005 22:32:50 -0000 1.2 --- src/include/storage/procarray.h 3 Jul 2005 22:52:30 -0000 *************** *** 31,36 **** --- 31,38 ---- 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); Index: src/tools/pgindent/pgindent =================================================================== RCS file: /projects/cvsroot/pgsql/src/tools/pgindent/pgindent,v retrieving revision 1.75 diff -c -r1.75 pgindent *** src/tools/pgindent/pgindent 28 Jun 2005 23:55:30 -0000 1.75 --- src/tools/pgindent/pgindent 3 Jul 2005 22:53:03 -0000 *************** *** 177,182 **** --- 177,183 ---- -TAllocSetContext \ -TAllocateDesc \ -TAllocateDescKind \ + -TAlterDatabaseStmt \ -TAlterDatabaseSetStmt \ -TAlterDomainStmt \ -TAlterFunctionStmt \