Ask specific +timezone+ from server.
+== SECTION [users] ==
+
+This contains key=value pairs where key will be taken as a user name and
+value as a libpq connect-string style list of key=value pairs. As actual libpq is not
+used, so not all features from libpq can be used.
+
+=== Pool configuration ===
+
+==== pool_mode ====
+
+Set the pool mode to be used for all connections from this user. If not set, the
+database or default pool_mode is used.
+
== AUTHENTICATION FILE FORMAT ==
PgBouncer needs its own user database. The users are loaded from a text
* hba-style access control
- * per-db pool_mode
-
- * per-user pool_mode, other settings ([users], user=connstr?)
+ * other per-user settings
* identd authentication
not handle requests quick enough. Reason may be either overloaded
server or just too small of a +pool_size+ setting.
+pool_mode::
+ The pooling mode in use.
==== SHOW LISTS; ====
==== SHOW USERS; ====
-Shows one line per user, under the +name+ column name.
+name::
+ The user name
+
+pool_mode::
+ The user's override pool_mode, or NULL if the default will be used instead.
==== SHOW DATABASES; ====
Maximum number of server connections.
pool_mode::
- The pooling mode in user for this database.
+ The database's override pool_mode, or NULL if the default will be used instead.
==== SHOW FDS; ====
struct AANode tree_node; /* used to attach user to tree */
char name[MAX_USERNAME];
char passwd[MAX_PASSWORD];
+ int pool_mode;
};
/*
/* connstring parsing */
bool parse_database(void *base, const char *name, const char *connstr);
+bool parse_user(void *base, const char *name, const char *params);
+
/* user file parsing */
bool load_auth_file(const char *fn) /* _MUSTCHECK */;
bool loader_users_check(void) /* _MUSTCHECK */;
*/
bool server_proto(SBuf *sbuf, SBufEvent evtype, struct MBuf *pkt) _MUSTCHECK;
-
+int pool_pool_mode(PgPool *pool) _MUSTCHECK;
const char *f_user;
PktBuf *buf;
struct CfValue cv;
- int pool_mode;
+ const char *pool_mode_str;
+ cv.extra = pool_mode_map;
buf = pktbuf_dynamic(256);
if (!buf) {
admin_error(admin, "no mem");
db = container_of(item, PgDatabase, head);
f_user = db->forced_user ? db->forced_user->name : NULL;
- pool_mode = db->pool_mode;
- if (pool_mode == POOL_INHERIT)
- pool_mode = cf_pool_mode;
- cv.value_p = &pool_mode;
- cv.extra = pool_mode_map;
+ pool_mode_str = NULL;
+ cv.value_p = &db->pool_mode;
+ if (db->pool_mode != POOL_INHERIT)
+ pool_mode_str = cf_get_lookup(&cv);
pktbuf_write_DataRow(buf, "ssissiis",
db->name, db->host, db->port,
db->dbname, f_user,
db->pool_size,
db->res_pool_size,
- cf_get_lookup(&cv));
+ pool_mode_str);
}
admin_flush(admin, buf, "SHOW");
return true;
PgUser *user;
struct List *item;
PktBuf *buf = pktbuf_dynamic(256);
+ struct CfValue cv;
+ const char *pool_mode_str;
+
if (!buf) {
admin_error(admin, "no mem");
return true;
}
- pktbuf_write_RowDescription(buf, "s", "name");
+ cv.extra = pool_mode_map;
+
+ pktbuf_write_RowDescription(buf, "ss", "name", "pool_mode");
statlist_for_each(item, &user_list) {
user = container_of(item, PgUser, head);
- pktbuf_write_DataRow(buf, "s", user->name);
+ pool_mode_str = NULL;
+ cv.value_p = &user->pool_mode;
+ if (user->pool_mode != POOL_INHERIT)
+ pool_mode_str = cf_get_lookup(&cv);
+
+ pktbuf_write_DataRow(buf, "ss", user->name, pool_mode_str);
}
admin_flush(admin, buf, "SHOW");
return true;
PktBuf *buf;
PgSocket *waiter;
usec_t now = get_cached_time();
+ struct CfValue cv;
+ int pool_mode;
+ cv.extra = pool_mode_map;
+ cv.value_p = &pool_mode;
buf = pktbuf_dynamic(256);
if (!buf) {
admin_error(admin, "no mem");
return true;
}
- pktbuf_write_RowDescription(buf, "ssiiiiiiii",
+ pktbuf_write_RowDescription(buf, "ssiiiiiiiis",
"database", "user",
"cl_active", "cl_waiting",
"sv_active", "sv_idle",
"sv_used", "sv_tested",
- "sv_login", "maxwait");
+ "sv_login", "maxwait",
+ "pool_mode");
statlist_for_each(item, &pool_list) {
pool = container_of(item, PgPool, head);
waiter = first_socket(&pool->waiting_client_list);
- pktbuf_write_DataRow(buf, "ssiiiiiiii",
+ pool_mode = pool_pool_mode(pool);
+ pktbuf_write_DataRow(buf, "ssiiiiiiiis",
pool->db->name, pool->user->name,
statlist_count(&pool->active_client_list),
statlist_count(&pool->waiting_client_list),
statlist_count(&pool->new_server_list),
/* how long is the oldest client waited */
(waiter && waiter->query_start)
- ? (int)((now - waiter->query_start) / USEC) : 0);
+ ? (int)((now - waiter->query_start) / USEC) : 0,
+ cf_get_lookup(&cv));
}
admin_flush(admin, buf, "SHOW");
return true;
return true;
}
+bool parse_user(void *base, const char *name, const char *connstr)
+{
+ char *p, *key, *val, *tmp_connstr;
+ PgUser *user;
+ struct CfValue cv;
+ int pool_mode = POOL_INHERIT;
+
+ cv.value_p = &pool_mode;
+ cv.extra = (const void *)pool_mode_map;
+
+ tmp_connstr = strdup(connstr);
+ if (!tmp_connstr)
+ return false;
+
+ p = tmp_connstr;
+ while (*p) {
+ p = cstr_get_pair(p, &key, &val);
+ if (p == NULL) {
+ log_error("%s: syntax error in user settings", name);
+ goto fail;
+ } else if (!key[0])
+ break;
+
+ if (strcmp("pool_mode", key) == 0) {
+ if (!cf_set_lookup(&cv, val)) {
+ log_error("skipping user %s because"
+ " of invalid pool mode: %s", name, val);
+ goto fail;
+ }
+ } else {
+ log_error("skipping user %s because"
+ " of unknown parameter in settings: %s", name, key);
+ goto fail;
+ }
+ }
+
+ user = find_user(name);
+ if (!user) {
+ user = add_user(name, "");
+ if (!user) {
+ log_error("cannot create user, no memory?");
+ goto fail;
+ }
+ }
+
+ user->pool_mode = pool_mode;
+
+fail:
+ free(tmp_connstr);
+ return true;
+}
+
/*
* User file parsing
*/
}, {
.sect_name = "databases",
.set_key = parse_database,
+ }, {
+ .sect_name = "users",
+ .set_key = parse_user,
}, {
.sect_name = NULL,
}
put_in_order(&user->head, &user_list, cmp_user);
aatree_insert(&user_tree, (uintptr_t)user->name, &user->tree_node);
+ user->pool_mode = POOL_INHERIT;
}
safe_strcpy(user->passwd, passwd, sizeof(user->passwd));
return user;
return NULL;
list_init(&user->head);
list_init(&user->pool_list);
+ user->pool_mode = POOL_INHERIT;
}
safe_strcpy(user->name, name, sizeof(user->name));
safe_strcpy(user->passwd, passwd, sizeof(user->passwd));
return res;
}
+int pool_pool_mode(PgPool *pool)
+{
+ int pool_mode = pool->user->pool_mode;
+ if (pool_mode == POOL_INHERIT)
+ pool_mode = pool->db->pool_mode;
+ if (pool_mode == POOL_INHERIT)
+ pool_mode = cf_pool_mode;
+ return pool_mode;
+}
+
/* process packets on logged in connection */
static bool handle_server_work(PgSocket *server, PktHdr *pkt)
{
return false;
/* set ready only if no tx */
- if (state == 'I') {
+ if (state == 'I')
ready = true;
- } else if (server->pool->db->pool_mode == POOL_STMT ||
- (server->pool->db->pool_mode == POOL_INHERIT && cf_pool_mode == POOL_STMT)) {
+ else if (pool_pool_mode(server->pool) == POOL_STMT) {
disconnect_server(server, true, "Long transactions not allowed");
return false;
} else if (state == 'T' || state == 'E') {
PgSocket *server = container_of(sbuf, PgSocket, sbuf);
PgPool *pool = server->pool;
PktHdr pkt;
- int pool_mode;
Assert(is_server_socket(server));
Assert(server->state != SV_FREE);
break;
}
- pool_mode = server->pool->db->pool_mode;
- if (pool_mode == POOL_INHERIT)
- pool_mode = cf_pool_mode;
- if (pool_mode != POOL_SESSION || server->state == SV_TESTED) {
+ if (pool_pool_mode(pool) != POOL_SESSION || server->state == SV_TESTED) {
switch (server->state) {
case SV_ACTIVE:
case SV_TESTED: