Re: per user/database connections limit again - Mailing list pgsql-patches
From | Bruce Momjian |
---|---|
Subject | Re: per user/database connections limit again |
Date | |
Msg-id | 200507022028.j62KSm708594@candle.pha.pa.us Whole thread Raw |
In response to | per user/database connections limit again (Petr Jelinek <pjmodos@parba.cz>) |
Responses |
Re: per user/database connections limit again
|
List | pgsql-patches |
We will need these: > Patch includes only changes to backend, I will make pg_dump, ecpg and > documentation patches once this is completed and accepted by team. Your patch has been added to the PostgreSQL unapplied patches list at: http://momjian.postgresql.org/cgi-bin/pgpatches It will be applied as soon as one of the PostgreSQL committers reviews and approves it. --------------------------------------------------------------------------- Petr Jelinek wrote: > Hi, > > I attached second try of per-database and per-user connection limit for > your review. > > This time I am using information stored in ProcArray to get number of > connections - I modified PGPROC struct to also include userid. > > Limits for user and database are stored in catalog tables. This aproach > led to implementation of "universal" ALTER DATABASE query (I followed > ALTER USER ad ALTER DATABASE ... RENAME implementatons). So queries for > setting maximum connections look like this: CREATE|ALTER DATABASE|USER > name MAX CONNECTIONS = 20; > Maximum connections defaults to zero which means unlimited (limited by > global maximum only) and isn't enforced for superusers. > > The actual check for maximum conenctions is done in ReverifyMyDatabase > for database and InitializeSessionUser for user because we don't have > information from system catalog before so we don't know how many > connections are allowed. > > Patch includes only changes to backend, I will make pg_dump, ecpg and > documentation patches once this is completed and accepted by team. > > Diff is made against cvs from today morning GMT (apply with -p1 if you > want to test it) - cvs is down now so I can't make diff against repository. > > -- > Regards > Petr Jelinek (PJMODOS) > > > diff -Nacr my-cvs/src/backend/commands/dbcommands.c my-aproach2/src/backend/commands/dbcommands.c > *** my-cvs/src/backend/commands/dbcommands.c Sun Jun 26 00:47:30 2005 > --- my-aproach2/src/backend/commands/dbcommands.c Tue Jun 28 11:26:08 2005 > *************** > *** 53,60 **** > > /* non-export function prototypes */ > static bool get_db_info(const char *name, Oid *dbIdP, int4 *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, int4 *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 sysid 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); > *************** > *** 588,594 **** > */ > pgdbrel = heap_open(DatabaseRelationId, ExclusiveLock); > > ! if (!get_db_info(dbname, &db_id, &db_owner, NULL, > &db_istemplate, NULL, NULL, NULL, NULL, NULL)) > ereport(ERROR, > (errcode(ERRCODE_UNDEFINED_DATABASE), > --- 607,613 ---- > */ > pgdbrel = heap_open(DatabaseRelationId, ExclusiveLock); > > ! if (!get_db_info(dbname, &db_id, &db_owner, NULL, NULL, > &db_istemplate, NULL, NULL, NULL, NULL, NULL)) > ereport(ERROR, > (errcode(ERRCODE_UNDEFINED_DATABASE), > *************** > *** 784,789 **** > --- 803,894 ---- > > > /* > + * 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 (!(superuser() > + || ((Form_pg_database) GETSTRUCT(tuple))->datdba == 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 (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 > *************** > *** 973,980 **** > > static bool > get_db_info(const char *name, Oid *dbIdP, int4 *ownerIdP, > ! int *encodingP, bool *dbIsTemplateP, bool *dbAllowConnP, > ! Oid *dbLastSysOidP, > TransactionId *dbVacuumXidP, TransactionId *dbFrozenXidP, > Oid *dbTablespace) > { > --- 1078,1085 ---- > > static bool > get_db_info(const char *name, Oid *dbIdP, int4 *ownerIdP, > ! int *encodingP, int *dbMaxConnP, bool *dbIsTemplateP, > ! bool *dbAllowConnP, Oid *dbLastSysOidP, > TransactionId *dbVacuumXidP, TransactionId *dbFrozenXidP, > Oid *dbTablespace) > { > *************** > *** 1019,1024 **** > --- 1124,1132 ---- > /* 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; > diff -Nacr my-cvs/src/backend/commands/user.c my-aproach2/src/backend/commands/user.c > *** my-cvs/src/backend/commands/user.c Thu Apr 14 22:03:24 2005 > --- my-aproach2/src/backend/commands/user.c Tue Jun 28 11:26:18 2005 > *************** > *** 64,69 **** > --- 64,70 ---- > int sysid = 0; /* PgSQL system id (valid if havesysid) */ > bool createdb = false; /* Can the user create databases? */ > bool createuser = false; /* Can this user create users? */ > + int maxconn = false; /* maximum connections allowed */ > List *groupElts = NIL; /* The groups the user is a member of */ > char *validUntil = NULL; /* The time the login is valid > * until */ > *************** > *** 73,78 **** > --- 74,80 ---- > DefElem *dcreateuser = NULL; > DefElem *dgroupElts = NULL; > DefElem *dvalidUntil = NULL; > + DefElem *dmaxconn = NULL; > > /* Extract options from the statement node tree */ > foreach(option, stmt->options) > *************** > *** 117,122 **** > --- 119,132 ---- > errmsg("conflicting or redundant options"))); > dcreateuser = 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, "groupElts") == 0) > { > if (dgroupElts) > *************** > *** 142,147 **** > --- 152,165 ---- > createdb = intVal(dcreatedb->arg) != 0; > if (dcreateuser) > createuser = intVal(dcreateuser->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 (dsysid) > { > sysid = intVal(dsysid->arg); > *************** > *** 233,238 **** > --- 251,257 ---- > new_record[Anum_pg_shadow_usesuper - 1] = BoolGetDatum(createuser); > /* superuser gets catupd right by default */ > new_record[Anum_pg_shadow_usecatupd - 1] = BoolGetDatum(createuser); > + new_record[Anum_pg_shadow_usemaxconn - 1] = Int32GetDatum(maxconn); > > if (password) > { > *************** > *** 317,328 **** > --- 336,349 ---- > char encrypted_password[MD5_PASSWD_LEN + 1]; > int createdb = -1; /* Can the user create databases? */ > int createuser = -1; /* Can this user create users? */ > + int maxconn = -1; /* Maximum connections allowed */ > char *validUntil = NULL; /* The time the login is valid > * until */ > DefElem *dpassword = NULL; > DefElem *dcreatedb = NULL; > DefElem *dcreateuser = NULL; > DefElem *dvalidUntil = NULL; > + DefElem *dmaxconn = NULL; > > /* Extract options from the statement node tree */ > foreach(option, stmt->options) > *************** > *** 359,364 **** > --- 380,393 ---- > errmsg("conflicting or redundant options"))); > dcreateuser = 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, "validUntil") == 0) > { > if (dvalidUntil) > *************** > *** 376,381 **** > --- 405,412 ---- > createdb = intVal(dcreatedb->arg); > if (dcreateuser) > createuser = intVal(dcreateuser->arg); > + if (dmaxconn) > + maxconn = intVal(dmaxconn->arg); > if (dvalidUntil) > validUntil = strVal(dvalidUntil->arg); > if (dpassword) > *************** > *** 427,432 **** > --- 458,469 ---- > { > new_record[Anum_pg_shadow_usecreatedb - 1] = BoolGetDatum(createdb > 0); > new_record_repl[Anum_pg_shadow_usecreatedb - 1] = 'r'; > + } > + > + if (maxconn >= 0) > + { > + new_record[Anum_pg_shadow_usemaxconn - 1] = Int32GetDatum(maxconn); > + new_record_repl[Anum_pg_shadow_usemaxconn - 1] = 'r'; > } > > /* > diff -Nacr my-cvs/src/backend/nodes/copyfuncs.c my-aproach2/src/backend/nodes/copyfuncs.c > *** my-cvs/src/backend/nodes/copyfuncs.c Mon Jun 27 00:05:38 2005 > --- my-aproach2/src/backend/nodes/copyfuncs.c Tue Jun 28 06:07:50 2005 > *************** > *** 2191,2196 **** > --- 2191,2207 ---- > 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) > { > diff -Nacr my-cvs/src/backend/nodes/equalfuncs.c my-aproach2/src/backend/nodes/equalfuncs.c > *** my-cvs/src/backend/nodes/equalfuncs.c Mon Jun 27 00:05:38 2005 > --- my-aproach2/src/backend/nodes/equalfuncs.c Tue Jun 28 06:07:50 2005 > *************** > *** 1141,1146 **** > --- 1141,1155 ---- > } > > 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); > diff -Nacr my-cvs/src/backend/parser/gram.y my-aproach2/src/backend/parser/gram.y > *** my-cvs/src/backend/parser/gram.y Mon Jun 27 00:05:38 2005 > --- my-aproach2/src/backend/parser/gram.y Tue Jun 28 11:26:30 2005 > *************** > *** 131,139 **** > } > > %type <node> stmt schema_stmt > ! AlterDatabaseSetStmt AlterDomainStmt AlterGroupStmt AlterOwnerStmt > ! AlterSeqStmt AlterTableStmt AlterUserStmt AlterUserSetStmt > ! AnalyzeStmt ClosePortalStmt ClusterStmt CommentStmt > ConstraintsSetStmt CopyStmt CreateAsStmt CreateCastStmt > CreateDomainStmt CreateGroupStmt CreateOpClassStmt CreatePLangStmt > CreateSchemaStmt CreateSeqStmt CreateStmt CreateTableSpaceStmt > --- 131,139 ---- > } > > %type <node> stmt schema_stmt > ! AlterDatabaseStmt AlterDatabaseSetStmt AlterDomainStmt AlterGroupStmt > ! AlterOwnerStmt AlterSeqStmt AlterTableStmt AlterUserStmt > ! AlterUserSetStmt AnalyzeStmt ClosePortalStmt ClusterStmt CommentStmt > ConstraintsSetStmt CopyStmt CreateAsStmt CreateCastStmt > CreateDomainStmt CreateGroupStmt CreateOpClassStmt CreatePLangStmt > CreateSchemaStmt CreateSeqStmt CreateStmt CreateTableSpaceStmt > *************** > *** 164,171 **** > > %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 <ival> opt_lock lock_type cast_context > %type <boolean> opt_force opt_or_replace > --- 164,173 ---- > > %type <dbehavior> opt_drop_behavior > > ! %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 > *************** > *** 346,352 **** > 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 > CREATEUSER CROSS CSV CURRENT_DATE CURRENT_TIME > CURRENT_TIMESTAMP CURRENT_USER CURSOR CYCLE > > --- 348,354 ---- > 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 > CREATEUSER CROSS CSV CURRENT_DATE CURRENT_TIME > CURRENT_TIMESTAMP CURRENT_USER CURSOR CYCLE > > *************** > *** 377,383 **** > LIKE LIMIT LISTEN LOAD LOCAL LOCALTIME LOCALTIMESTAMP LOCATION > LOCK_P > > ! MATCH MAXVALUE MINUTE_P MINVALUE MODE MONTH_P MOVE > > NAMES NATIONAL NATURAL NCHAR NEW NEXT NO NOCREATEDB > NOCREATEUSER NONE NOT NOTHING NOTIFY NOTNULL NOWAIT NULL_P > --- 379,385 ---- > LIKE LIMIT LISTEN LOAD LOCAL LOCALTIME LOCALTIMESTAMP LOCATION > LOCK_P > > ! MATCH MAX MAXVALUE MINUTE_P MINVALUE MODE MONTH_P MOVE > > NAMES NATIONAL NATURAL NCHAR NEW NEXT NO NOCREATEDB > NOCREATEUSER NONE NOT NOTHING NOTIFY NOTNULL NOWAIT NULL_P > *************** > *** 490,496 **** > ; > > stmt : > ! AlterDatabaseSetStmt > | AlterDomainStmt > | AlterFunctionStmt > | AlterGroupStmt > --- 492,499 ---- > ; > > stmt : > ! AlterDatabaseStmt > ! | AlterDatabaseSetStmt > | AlterDomainStmt > | AlterFunctionStmt > | AlterGroupStmt > *************** > *** 688,693 **** > --- 691,700 ---- > { > $$ = makeDefElem("createuser", (Node *)makeInteger(FALSE)); > } > + | MAX CONNECTIONS Iconst > + { > + $$ = makeDefElem("maxconnections", (Node *)makeInteger($3)); > + } > | IN_P GROUP_P user_list > { > $$ = makeDefElem("groupElts", (Node *)$3); > *************** > *** 4294,4299 **** > --- 4301,4310 ---- > { > $$ = makeDefElem("encoding", NULL); > } > + | MAX CONNECTIONS opt_equal Iconst > + { > + $$ = makeDefElem("maxconnections", (Node *)makeInteger($4)); > + } > | OWNER opt_equal name > { > $$ = makeDefElem("owner", (Node *)makeString($3)); > *************** > *** 4320,4325 **** > --- 4331,4346 ---- > * > *****************************************************************************/ > > + 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 > { > *************** > *** 4340,4345 **** > --- 4361,4379 ---- > ; > > > + 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 > *************** > *** 7770,7775 **** > --- 7804,7810 ---- > | COMMENT > | COMMIT > | COMMITTED > + | CONNECTIONS > | CONSTRAINTS > | CONVERSION_P > | COPY > *************** > *** 7835,7840 **** > --- 7870,7876 ---- > | LOCATION > | LOCK_P > | MATCH > + | MAX > | MAXVALUE > | MINUTE_P > | MINVALUE > diff -Nacr my-cvs/src/backend/parser/keywords.c my-aproach2/src/backend/parser/keywords.c > *** my-cvs/src/backend/parser/keywords.c Mon Jun 27 00:05:40 2005 > --- my-aproach2/src/backend/parser/keywords.c Tue Jun 28 06:07:50 2005 > *************** > *** 82,87 **** > --- 82,88 ---- > {"comment", COMMENT}, > {"commit", COMMIT}, > {"committed", COMMITTED}, > + {"connections", CONNECTIONS}, > {"constraint", CONSTRAINT}, > {"constraints", CONSTRAINTS}, > {"conversion", CONVERSION_P}, > *************** > *** 198,203 **** > --- 199,205 ---- > {"location", LOCATION}, > {"lock", LOCK_P}, > {"match", MATCH}, > + {"max", MAX}, > {"maxvalue", MAXVALUE}, > {"minute", MINUTE_P}, > {"minvalue", MINVALUE}, > diff -Nacr my-cvs/src/backend/storage/ipc/procarray.c my-aproach2/src/backend/storage/ipc/procarray.c > *** my-cvs/src/backend/storage/ipc/procarray.c Sat Jun 18 00:32:46 2005 > --- my-aproach2/src/backend/storage/ipc/procarray.c Tue Jun 28 06:07:50 2005 > *************** > *** 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(AclId userid) > + { > + 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->userId == userid) > + count++; > + } > + > + LWLockRelease(ProcArrayLock); > + > + return count; > + } > + > + > #define XidCacheRemove(i) \ > do { \ > MyProc->subxids.xids[i] = MyProc->subxids.xids[MyProc->subxids.nxids - 1]; \ > diff -Nacr my-cvs/src/backend/storage/lmgr/proc.c my-aproach2/src/backend/storage/lmgr/proc.c > *** my-cvs/src/backend/storage/lmgr/proc.c Sat Jun 18 00:32:46 2005 > --- my-aproach2/src/backend/storage/lmgr/proc.c Tue Jun 28 06:39:46 2005 > *************** > *** 254,259 **** > --- 254,260 ---- > MyProc->xmin = InvalidTransactionId; > MyProc->pid = MyProcPid; > MyProc->databaseId = MyDatabaseId; > + MyProc->userId = GetSessionUserId(); > MyProc->lwWaiting = false; > MyProc->lwExclusive = false; > MyProc->lwWaitLink = NULL; > diff -Nacr my-cvs/src/backend/tcop/utility.c my-aproach2/src/backend/tcop/utility.c > *** my-cvs/src/backend/tcop/utility.c Wed Jun 22 23:14:30 2005 > --- my-aproach2/src/backens/tcop/utility.c Tue Jun 28 06:07:50 2005 > *************** > *** 276,281 **** > --- 276,282 ---- > > switch (nodeTag(parsetree)) > { > + case T_AlterDatabaseStmt: > case T_AlterDatabaseSetStmt: > case T_AlterDomainStmt: > case T_AlterFunctionStmt: > *************** > *** 786,791 **** > --- 787,796 ---- > > case T_CreatedbStmt: > createdb((CreatedbStmt *) parsetree); > + break; > + > + case T_AlterDatabaseStmt: > + AlterDatabase((AlterDatabaseStmt *) parsetree); > break; > > case T_AlterDatabaseSetStmt: > diff -Nacr my-cvs/src/backend/utils/init/miscinit.c my-aproach2/src/backend/utils/init/miscinit.c > *** my-cvs/src/backend/utils/init/miscinit.c Mon Jun 20 04:17:30 2005 > --- my-aproach2/src/backend/utils/init/miscinit.c Tue Jun 28 06:41:40 2005 > *************** > *** 315,320 **** > --- 315,321 ---- > Datum datum; > bool isnull; > AclId usesysid; > + Form_pg_shadow userform; > > /* > * Don't do scans if we're bootstrapping, none of the system catalogs > *************** > *** 333,344 **** > (errcode(ERRCODE_UNDEFINED_OBJECT), > errmsg("user \"%s\" does not exist", username))); > > ! usesysid = ((Form_pg_shadow) GETSTRUCT(userTup))->usesysid; > > AuthenticatedUserId = usesysid; > ! AuthenticatedUserIsSuperuser = ((Form_pg_shadow) GETSTRUCT(userTup))->usesuper; > > SetSessionUserId(usesysid); /* sets CurrentUserId too */ > > /* Record username and superuser status as GUC settings too */ > SetConfigOption("session_authorization", username, > --- 334,358 ---- > (errcode(ERRCODE_UNDEFINED_OBJECT), > errmsg("user \"%s\" does not exist", username))); > > ! userform = ((Form_pg_shadow) GETSTRUCT(userTup)); > ! usesysid = userform->usesysid; > > AuthenticatedUserId = usesysid; > ! AuthenticatedUserIsSuperuser = userform->usesuper; > > SetSessionUserId(usesysid); /* sets CurrentUserId too */ > + > + /* > + * Check connection limit for user > + */ > + if (userform->usemaxconn > 0 && !AuthenticatedUserIsSuperuser && > + CountUserBackends(AuthenticatedUserId) > userform->usemaxconn) > + { > + ereport(FATAL, > + (errcode(ERRCODE_TOO_MANY_CONNECTIONS), > + errmsg("sorry, too many clients already for user \"%s\"", > + username))); > + } > > /* Record username and superuser status as GUC settings too */ > SetConfigOption("session_authorization", username, > diff -Nacr my-cvs/src/backend/utils/init/postinit.c my-aproach2/src/backend/utils/init/postinit.c > *** my-cvs/src/backend/utils/init/postinit.c Fri Jun 24 03:06:26 2005 > --- my-aproach2/src/backend/utils/init/postinit.c Tue Jun 28 10:11:10 2005 > *************** > *** 50,55 **** > --- 50,56 ---- > static void InitCommunication(void); > static void ShutdownPostgres(int code, Datum arg); > static bool ThereIsAtLeastOneUser(void); > + static bool FindMyUser(const char *name, AclId *user_id); > > > /*** InitPostgres support ***/ > *************** > *** 100,105 **** > --- 101,137 ---- > } > > /* > + * Get user id from flatfiles > + * > + * We need this because we need to know userid before > + * InitProcess() is called > + */ > + static bool > + FindMyUser(const char *name, AclId *user_id) > + { > + List **line; > + ListCell *token; > + char thisname[NAMEDATALEN]; > + > + if ((line = get_user_line(name)) == NULL) > + ereport(FATAL, > + (ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION, /* ? */ > + errmsg("could not find user \"%s\": %m", name))); > + > + /* Skip over username */ > + token = list_head(*line); > + if (token) > + token = lnext(token); > + if (token) > + { > + *user_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 > *************** > *** 165,181 **** > 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 > --- 197,231 ---- > 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 > *************** > *** 350,355 **** > --- 400,424 ---- > * Code after this point assumes we are in the proper directory! > */ > > + /* > + * We need to know userid in InitProcess() so we have read it from > + * flatfile, real user inicialization is done later > + */ > + if (IsUnderPostmaster) > + { > + AclId userid; > + > + if (!FindMyUser(username, &userid)) > + ereport(FATAL, > + (ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION, /* ? */ > + errmsg("user \"%s\" does not exist", > + username))); > + > + SetSessionUserId(userid); > + } > + else > + SetSessionUserId(BOOTSTRAP_USESYSID); > + > /* > * 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 > diff -Nacr my-cvs/src/include/catalog/pg_database.h my-aproach2/src/include/catalog/pg_database.h > *** my-cvs/src/include/catalog/pg_database.h Thu Apr 14 03:38:20 2005 > --- my-aproach2/src/include/catalog/pg_database.h Tue Jun 28 06:07:50 2005 > *************** > *** 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 > > diff -Nacr my-cvs/src/include/catalog/pg_shadow.h my-aproach2/src/include/catalog/pg_shadow.h > *** my-cvs/src/include/catalog/pg_shadow.h Thu Apr 14 03:38:22 2005 > --- my-aproach2/src/include/catalog/pg_shadow.h Tue Jun 28 06:07:50 2005 > *************** > *** 36,41 **** > --- 36,42 ---- > bool usecreatedb; > bool usesuper; /* read this field via superuser() only */ > bool usecatupd; > + int4 usemaxconn; /* maximum connections allowed */ > > /* remaining fields may be null; use heap_getattr to read them! */ > text passwd; > *************** > *** 54,68 **** > * compiler constants for pg_shadow > * ---------------- > */ > ! #define Natts_pg_shadow 8 > #define Anum_pg_shadow_usename 1 > #define Anum_pg_shadow_usesysid 2 > #define Anum_pg_shadow_usecreatedb 3 > #define Anum_pg_shadow_usesuper 4 > #define Anum_pg_shadow_usecatupd 5 > ! #define Anum_pg_shadow_passwd 6 > ! #define Anum_pg_shadow_valuntil 7 > ! #define Anum_pg_shadow_useconfig 8 > > /* ---------------- > * initial contents of pg_shadow > --- 55,70 ---- > * compiler constants for pg_shadow > * ---------------- > */ > ! #define Natts_pg_shadow 9 > #define Anum_pg_shadow_usename 1 > #define Anum_pg_shadow_usesysid 2 > #define Anum_pg_shadow_usecreatedb 3 > #define Anum_pg_shadow_usesuper 4 > #define Anum_pg_shadow_usecatupd 5 > ! #define Anum_pg_shadow_usemaxconn 6 > ! #define Anum_pg_shadow_passwd 7 > ! #define Anum_pg_shadow_valuntil 8 > ! #define Anum_pg_shadow_useconfig 9 > > /* ---------------- > * initial contents of pg_shadow > *************** > *** 71,77 **** > * user choices. > * ---------------- > */ > ! DATA(insert ( "POSTGRES" PGUID t t t _null_ _null_ _null_ )); > > #define BOOTSTRAP_USESYSID 1 > > --- 73,79 ---- > * user choices. > * ---------------- > */ > ! DATA(insert ( "POSTGRES" PGUID t t t 0 _null_ _null_ _null_ )); > > #define BOOTSTRAP_USESYSID 1 > > diff -Nacr my-cvs/src/include/commands/dbcommands.h my-aproach2/src/include/commands/dbcommands.h > *** my-cvs/src/include/commands/dbcommands.h Mon Jun 06 19:01:26 2005 > --- my-aproach2/src/include/commands/dbcommands.h Tue Jun 28 06:07:50 2005 > *************** > *** 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, AclId newOwnerSysId); > > diff -Nacr my-cvs/src/include/nodes/nodes.h my-aproach2/src/include/nodes/nodes.h > *** my-cvs/src/include/nodes/nodes.h Mon Jun 27 00:05:42 2005 > --- my-aproach2/src/include/nodes/nodes.h Tue Jun 28 06:07:50 2005 > *************** > *** 272,277 **** > --- 272,278 ---- > T_ReindexStmt, > T_CheckPointStmt, > T_CreateSchemaStmt, > + T_AlterDatabaseStmt, > T_AlterDatabaseSetStmt, > T_AlterUserSetStmt, > T_CreateConversionStmt, > diff -Nacr my-cvs/src/include/nodes/parsenodes.h my-aproach2/src/include/nodes/parsenodes.h > *** my-cvs/src/include/nodes/parsenodes.h Wed Jun 22 23:14:32 2005 > --- my-aproach2/src/include/nodes/parsenodes.h Tue Jun 28 06:07:50 2005 > *************** > *** 1620,1625 **** > --- 1620,1632 ---- > * Alter Database > * ---------------------- > */ > + typedef struct AlterDatabaseStmt > + { > + NodeTag type; > + char *dbname; /* name of database to create */ > + List *options; /* List of DefElem nodes */ > + } AlterDatabaseStmt; > + > typedef struct AlterDatabaseSetStmt > { > NodeTag type; > diff -Nacr my-cvs/src/include/storage/proc.h my-aproach2/src/include/storage/proc.h > *** my-cvs/src/include/storage/proc.h Sat Jun 18 00:32:50 2005 > --- my-aproach2/src/include/storage/proc.h Tue Jun 28 06:07:50 2005 > *************** > *** 71,76 **** > --- 71,77 ---- > > int pid; /* This backend's process id, or 0 */ > Oid databaseId; /* OID of database this backend is using */ > + AclId userId; /* user connected to this backend */ > > /* Info about LWLock the process is currently waiting for, if any. */ > bool lwWaiting; /* true if waiting for an LW lock */ > diff -Nacr my-cvs/src/include/storage/procarray.h my-aproach2/src/include/storage/procarray.h > *** my-cvs/src/include/storage/procarray.h Sat Jun 18 00:32:50 2005 > --- my-aproach2/src/include/storage/procarray.h Tue Jun 28 06:07:50 2005 > *************** > *** 31,36 **** > --- 31,38 ---- > extern bool DatabaseHasActiveBackends(Oid databaseId, bool ignoreMyself); > > extern int CountActiveBackends(void); > + extern int CountDBBackends(Oid databaseid); > + extern int CountUserBackends(AclId userid); > > extern void XidCacheRemoveRunningXids(TransactionId xid, > int nxids, TransactionId *xids); > diff -Nacr my-cvs/src/tools/pgindent/pgindent my-aproach2/src/tools/pgindent/pgindent > *** my-cvs/src/tools/pgindent/pgindent Thu Oct 07 16:15:50 2004 > --- my-aproach2/src/tools/pgindent/pgindent Tue Jun 28 06:07:50 2005 > *************** > *** 175,180 **** > --- 175,181 ---- > -TAllocSetContext \ > -TAllocateDesc \ > -TAllocateDescKind \ > + -TAlterDatabaseStmt \ > -TAlterDatabaseSetStmt \ > -TAlterDomainStmt \ > -TAlterGroupStmt \ > > ---------------------------(end of broadcast)--------------------------- > TIP 3: if posting/reading through Usenet, please send an appropriate > subscribe-nomail command to majordomo@postgresql.org so that your > message can get through to the mailing list cleanly -- Bruce Momjian | http://candle.pha.pa.us pgman@candle.pha.pa.us | (610) 359-1001 + If your life is a hard drive, | 13 Roberts Road + Christ can be your backup. | Newtown Square, Pennsylvania 19073
pgsql-patches by date: