/* Copyright (c) 2010-2018 Dovecot authors, see the included COPYING file */
#include "lib.h"
#include "array.h"
#include "llist.h"
#include "ioloop.h"
#include "sql-api-private.h"
#include <time.h>
struct sqlpool_host {
char *connect_string;
unsigned int connection_count;
};
struct sqlpool_connection {
unsigned int host_idx;
};
struct sqlpool_db {
unsigned int connection_limit;
/* all connections from all hosts */
/* index of last connection in all_connections that was used to
send a query. */
unsigned int last_query_conn_idx;
/* queued requests */
};
struct sqlpool_request {
unsigned int host_idx;
unsigned int retry_count;
/* requests are a) queries */
char *query;
void *context;
/* b) transaction waiters */
};
struct sqlpool_transaction_context {
void *context;
};
extern struct sql_db driver_sqlpool_db;
static struct sqlpool_connection *
unsigned int host_idx);
static void
struct sqlpool_request *request);
static void
struct sqlpool_transaction_context *ctx);
{
return request;
}
static void
{
}
static void
{
}
static struct sql_transaction_context *
{
/* backend will use our queries list (we might still append more
queries to the list) */
return conn_trans;
}
static void
struct sqlpool_transaction_context *trans)
{
}
static void
{
return;
} else {
i_unreached();
}
}
{
(void)sql_connect(conndb);
}
static struct sqlpool_host *
unsigned int *host_idx_r)
{
unsigned int i, count;
*host_idx_r = 0;
for (i = 1; i < count; i++) {
*host_idx_r = i;
}
}
return min;
}
{
return TRUE;
}
return FALSE;
}
static void
{
unsigned int host_idx;
if (conndb->connect_failure_count > 0) {
/* increase delay between reconnections to this
server */
}
/* reconnect after the delay */
/* if we have zero successful hosts and there still are hosts
without connections, connect to one of them. */
if (!sqlpool_have_successful_connections(db)) {
if (host->connection_count == 0)
}
}
static void
void *context)
{
conndb->connect_failure_count = 0;
}
if (prev_state == SQL_DB_STATE_CONNECTING &&
}
static struct sqlpool_connection *
unsigned int host_idx)
{
host->connection_count++;
return conn;
}
static struct sqlpool_connection *
{
unsigned int host_idx;
return NULL;
else
}
static const struct sqlpool_connection *
unsigned int unwanted_host_idx,
bool *all_disconnected_r)
{
unsigned int i, count;
for (i = 0; i < count; i++) {
continue;
/* see if we could reconnect to it immediately */
(void)sql_connect(conndb);
}
if (SQL_DB_IS_READY(conndb)) {
}
}
return NULL;
}
static bool
unsigned int unwanted_host_idx,
const struct sqlpool_connection **conn_r)
{
unsigned int i, count;
bool all_disconnected;
/* maybe there are no wanted hosts. use any of them. */
}
/* no connected connections. connect_delays may have gotten too
high, reset all of them to see if some are still alive. */
for (i = 0; i < count; i++) {
}
}
/* still nothing. try creating new connections */
return FALSE;
}
return TRUE;
}
static bool
const struct sqlpool_connection **conn_r)
{
unsigned int i, count;
return TRUE;
/* no idling connections, but maybe we can find one that's trying to
connect to server, and we can use it once it's finished */
for (i = 0; i < count; i++) {
return TRUE;
}
}
return FALSE;
}
static void
{
/* connect string is a space separated list. it may contain
backend-specific strings which we'll pass as-is. we'll only care
about our own settings, plus the host settings. */
value = "";
} else {
value++;
}
i_fatal("Invalid value for maxconns: %s",
value);
}
} else {
}
}
/* build a new connect string without our settings or hosts */
if (array_count(&hostnames) == 0) {
/* no hosts specified. create a default one. */
} else {
if (*connect_string == '\0')
}
}
if (db->connection_limit == 0)
}
{
unsigned int host_idx;
for (;;) {
if (host->connection_count > 0)
break;
}
}
struct sql_db *
{
T_BEGIN {
} T_END;
/* connect to all databases so we can do load balancing immediately */
}
{
}
}
{
}
{
if (ret2 > 0)
ret = 1;
ret = 0;
}
return ret;
}
{
}
static const char *
{
unsigned int i, count;
/* use the first ready connection */
for (i = 0; i < count; i++) {
}
/* no ready connections. just use the first one (we're guaranteed
to always have one) */
}
{
break;
i_error("%s: Query timed out "
"(no free connections for %u secs): %s",
"<transaction>");
}
}
static void
struct sqlpool_request *request)
{
}
}
static void
struct sqlpool_request *request)
{
}
}
static void
struct sqlpool_request *request)
{
if (result->failed_try_retry &&
i_warning("%s: Query failed, retrying: %s",
request->retry_count++;
}
} else {
i_error("%s: Query failed, aborting: %s",
}
}
}
{
else {
request);
}
}
{
}
static struct sql_result *
{
return &sql_not_connected_result;
}
if (result->failed_try_retry) {
return result;
}
return result;
}
static struct sql_transaction_context *
{
/* queue changes until commit. even if we did have a free connection
now, don't use it or multiple open transactions could tie up all
connections. */
}
static void
{
}
static void
struct sqlpool_transaction_context *ctx)
{
}
static void
void *context)
{
(struct sqlpool_transaction_context *)_ctx;
else
}
static int
const char **error_r)
{
(struct sqlpool_transaction_context *)_ctx;
int ret;
return -1;
}
return ret;
}
static void
{
(struct sqlpool_transaction_context *)_ctx;
}
static void
unsigned int *affected_rows)
{
(struct sqlpool_transaction_context *)_ctx;
/* we didn't get a connection for transaction immediately.
queue updates until commit transfers all of these */
}
static const char *
{
unsigned int i, count;
/* use the first ready connection */
for (i = 0; i < count; i++) {
}
/* no ready connections. just use the first one (we're guaranteed
to always have one) */
}
"",
.v = {
}
};