driver-mysql.c revision 0db5b158a00c08955bdacc99b1e2cd1ec07f4311
/* Copyright (c) 2003-2010 Dovecot authors, see the included COPYING file */
#include "lib.h"
#include "array.h"
#include "str.h"
#include "sql-api-private.h"
#ifdef BUILD_MYSQL
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#include <mysql.h>
#include <errmsg.h>
/* Abort connect() if it can't connect within this time. */
#define MYSQL_CONNECT_FAILURE_TIMEOUT 10
/* Minimum delay between reconnecting to same server */
#define CONNECT_MIN_DELAY 1
/* Maximum time to avoiding reconnecting to same server */
/* If no servers are connected but a query is requested, try reconnecting to
next server which has been disconnected longer than this (with a single
server setup this is really the "max delay" and the CONNECT_MAX_DELAY
is never used). */
#define CONNECT_RESET_DELAY 15
struct mysql_db {
const char *option_file, *option_group;
unsigned int port, client_flags;
unsigned int next_query_connection;
};
struct mysql_connection {
const char *host;
unsigned int connect_delay;
unsigned int connect_failure_count;
unsigned int connected:1;
unsigned int ssl_set:1;
};
struct mysql_result {
struct sql_result api;
struct mysql_connection *conn;
unsigned int fields_count;
};
struct mysql_transaction_context {
struct sql_transaction_context ctx;
const char *error;
unsigned int failed:1;
};
struct mysql_query_list {
struct mysql_query_list *next;
const char *query;
unsigned int *affected_rows;
};
extern const struct sql_db driver_mysql_db;
extern const struct sql_result driver_mysql_result;
extern const struct sql_result driver_mysql_error_result;
{
const char *unix_socket, *host;
bool failed;
return TRUE;
/* don't try reconnecting more than once a second */
return FALSE;
} else {
unix_socket = NULL;
}
db->option_file);
}
#ifdef HAVE_MYSQL_SSL
#ifdef HAVE_MYSQL_SSL_CIPHER
, db->ssl_cipher
#endif
);
#else
i_fatal("mysql: SSL support not compiled in "
"(remove ssl_ca and ssl_ca_path settings)");
#endif
}
#ifdef CLIENT_MULTI_RESULTS
#endif
/* CLIENT_MULTI_RESULTS allows the use of stored procedures */
client_flags) == NULL;
alarm(0);
if (failed) {
if (conn->connect_failure_count > 0) {
/* increase delay between reconnections to this
server */
}
i_error("mysql: Connect failed to %s (%s): %s - "
"waiting for %u seconds before retry",
return FALSE;
} else {
i_info("mysql: Connected to %s%s (%s)",
conn->connect_failure_count = 0;
return TRUE;
}
}
{
struct mysql_connection *conn;
int ret = -1;
if (driver_mysql_connect(conn))
ret = 1;
}
return ret;
}
{
struct mysql_connection *conn;
i_fatal("mysql_init() failed");
}
{
}
const char *connect_string)
{
const char **field;
i_fatal("mysql: Missing value in connect string: %s",
*args);
}
value++;
else
}
i_fatal("mysql: No hosts given in connect string");
}
{
T_BEGIN {
} T_END;
}
{
struct mysql_connection *conn;
(void)driver_mysql_connection_free(conn);
}
static enum sql_db_flags
{
return SQL_DB_FLAG_BLOCKING;
}
const char *query)
{
int i;
for (i = 0; i < 2; i++) {
if (!driver_mysql_connect(conn))
return 0;
return 1;
/* failed */
case CR_SERVER_GONE_ERROR:
case CR_SERVER_LOST:
/* connection lost - try immediate reconnect */
break;
default:
return -1;
}
}
/* connected -> lost it -> connected -> lost again */
return 0;
}
struct mysql_connection **conn_r)
{
struct mysql_connection *conn;
bool reset;
int ret;
/* go through the connections in round robin. if the connection
isn't available, try next one that is. */
i = start;
do {
if (ret != 0) {
/* success / failure */
return ret;
}
/* not connected, try next one */
i = (i + 1) % count;
} while (i != start);
if (reset)
break;
/* none are connected. connect_delays may have gotten too high,
reset all of them to see if some are still alive. */
for (i = 0; i < count; i++)
}
return 0;
}
static const char *
{
struct mysql_connection *conn;
unsigned int i, count;
char *to;
/* All the connections should be identical, so just use the first
connected one */
for (i = 0; i < count; i++) {
break;
}
if (i == count) {
/* so, try connecting.. */
for (i = 0; i < count; i++) {
if (driver_mysql_connect(&conn[i]))
break;
}
if (i == count) {
/* FIXME: we don't have a valid connection, so fallback
to using default escaping. the next query will most
likely fail anyway so it shouldn't matter that much
what we return here.. Anyway, this API needs
changing so that the escaping function could already
fail the query reliably. */
return to;
}
}
return to;
}
{
struct mysql_connection *conn;
}
{
struct sql_result *result;
}
static struct sql_result *
{
struct mysql_connection *conn;
struct mysql_result *result;
int ret;
case 0:
/* not connected */
break;
case 1:
/* query ok */
#ifdef CLIENT_MULTI_RESULTS
/* Because we've enabled CLIENT_MULTI_RESULTS, we need to read
(ignore) extra results - there should not be any.
ret is: -1 = done, >0 = error, 0 = more results. */
#else
ret = -1;
#endif
if (ret < 0 &&
break;
/* failed */
/* fall through */
case -1:
/* error */
break;
}
}
{
return;
}
{
/* no results */
return 0;
}
return 1;
}
{
return;
}
static unsigned int
{
return result->fields_count;
}
static const char *
{
}
const char *field_name)
{
unsigned int i;
for (i = 0; i < result->fields_count; i++) {
return i;
}
return -1;
}
static const char *
unsigned int idx)
{
}
static const unsigned char *
unsigned int idx ATTR_UNUSED,
{
/* FIXME */
return NULL;
}
static const char *
const char *field_name)
{
int idx;
if (idx < 0)
return NULL;
}
static const char *const *
{
}
{
}
static struct sql_transaction_context *
{
struct mysql_transaction_context *ctx;
}
static void
{
const char *error;
else
}
const char *query)
{
struct sql_result *result;
int ret = 0;
return -1;
if (sql_result_next_row(result) < 0) {
ret = -1;
}
return ret;
}
static int
const char **error_r)
{
struct mysql_transaction_context *ctx =
(struct mysql_transaction_context *)_ctx;
int ret = 0;
/* try to use a transaction in any case,
even if it doesn't work. */
break;
}
}
return ret;
}
static void
{
struct mysql_transaction_context *ctx =
(struct mysql_transaction_context *)_ctx;
}
static void
unsigned int *affected_rows)
{
struct mysql_transaction_context *ctx =
(struct mysql_transaction_context *)_ctx;
struct mysql_query_list *list;
else
}
const struct sql_db driver_mysql_db = {
"mysql",
.v = {
}
};
const struct sql_result driver_mysql_result = {
.v = {
}
};
static int
{
return -1;
}
const struct sql_result driver_mysql_error_result = {
.v = {
}
};
void driver_mysql_init(void);
void driver_mysql_deinit(void);
void driver_mysql_init(void)
{
}
void driver_mysql_deinit(void)
{
}
#endif