driver-mysql.c revision 5ab2ee0b9b7ad3867fcfd2a31fda0790370fbbbd
/* Copyright (C) 2003-2004 Timo Sirainen, Alex Howansky */
#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 {
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;
};
extern struct sql_db driver_mysql_db;
extern struct sql_result driver_mysql_result;
extern 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;
}
#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
}
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;
unsigned int i, count;
int ret = -1;
for (i = 0; i < count; i++) {
if (driver_mysql_connect(&conn[i]))
ret = 1;
}
return ret;
}
{
struct mysql_connection *conn;
i_fatal("mysql_init() failed");
}
{
}
const char *connect_string)
{
const char **field;
t_push();
i_fatal("mysql: Missing value in connect string: %s",
*args);
}
value++;
else
}
t_pop();
i_fatal("mysql: No hosts given in connect string");
}
{
}
{
struct mysql_connection *conn;
unsigned int i, count;
for (i = 0; i < count; i++)
(void)driver_mysql_connection_free(&conn[i]);
}
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;
}
{
struct mysql_connection *conn;
}
{
struct sql_result *result;
}
static struct sql_result *
{
struct mysql_connection *conn;
struct mysql_result *result;
case 0:
/* not connected */
break;
case 1:
/* query ok */
break;
/* fallback */
case -1:
/* error */
break;
}
}
{
return;
}
{
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 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
}
static int
const char **error_r)
{
struct mysql_transaction_context *ctx =
(struct mysql_transaction_context *)_ctx;
struct sql_result *result;
int ret = 0;
if (sql_result_next_row(result) < 0) {
ret = -1;
}
}
return ret;
}
static void
{
struct mysql_transaction_context *ctx =
(struct mysql_transaction_context *)_ctx;
}
static void
{
struct mysql_transaction_context *ctx =
(struct mysql_transaction_context *)_ctx;
/* FIXME: with mysql we're just appending everything into one big
string which gets committed in sql_transaction_commit(). we could
avoid this if we knew for sure that transactions actually worked,
but I don't know how to do that.. */
/* try to use a transaction in any case,
even if it doesn't work. */
}
}
struct sql_db driver_mysql_db = {
"mysql",
};
struct sql_result driver_mysql_result = {
NULL,
};
static int
{
return -1;
}
struct sql_result driver_mysql_error_result = {
NULL,
};
void driver_mysql_init(void);
void driver_mysql_deinit(void);
void driver_mysql_init(void)
{
}
void driver_mysql_deinit(void)
{
}
#endif