driver-mysql.c revision 5fbf8719b9ef072295c16bc4492f9f0ece92117d
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen/* Copyright (c) 2003-2010 Dovecot authors, see the included COPYING file */
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen#include "lib.h"
16f816d3f3c32ae3351834253f52ddd0212bcbf3Timo Sirainen#include "array.h"
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen#include "str.h"
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen#include "sql-api-private.h"
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen#ifdef BUILD_MYSQL
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen#include <stdlib.h>
faed8babca9914257f34fb2e603d74016d563b2dTimo Sirainen#include <unistd.h>
faed8babca9914257f34fb2e603d74016d563b2dTimo Sirainen#include <time.h>
faed8babca9914257f34fb2e603d74016d563b2dTimo Sirainen#include <mysql.h>
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen#include <errmsg.h>
faed8babca9914257f34fb2e603d74016d563b2dTimo Sirainen
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainenstruct mysql_db {
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainen struct sql_db api;
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen
190237ce467d2389dfb809874b0fec86d3c7968dTimo Sirainen pool_t pool;
b0be0bead3d6963149f7f2a9504b8ab5aced9af5Timo Sirainen const char *user, *password, *dbname, *host, *unix_socket;
41e1c7380edda701719d8ce1fb4d465d2ec4c84dTimo Sirainen const char *ssl_cert, *ssl_key, *ssl_ca, *ssl_ca_path, *ssl_cipher;
41e1c7380edda701719d8ce1fb4d465d2ec4c84dTimo Sirainen const char *option_file, *option_group;
8f7b00599e73fe71b1d2c6c65f8ae98aac1b23fbTimo Sirainen unsigned int port, client_flags;
8f7b00599e73fe71b1d2c6c65f8ae98aac1b23fbTimo Sirainen
6825360d446542046757b06064282301c4c6b27cTimo Sirainen MYSQL *mysql;
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainen unsigned int next_query_connection;
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainen
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainen unsigned int ssl_set:1;
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainen};
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainen
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainenstruct mysql_result {
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainen struct sql_result api;
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen MYSQL_RES *result;
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen MYSQL_ROW row;
faed8babca9914257f34fb2e603d74016d563b2dTimo Sirainen
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen MYSQL_FIELD *fields;
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen unsigned int fields_count;
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen my_ulonglong affected_rows;
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen};
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainenstruct mysql_transaction_context {
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen struct sql_transaction_context ctx;
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen pool_t query_pool;
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen const char *error;
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen unsigned int failed:1;
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen};
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainenextern const struct sql_db driver_mysql_db;
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainenextern const struct sql_result driver_mysql_result;
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainenextern const struct sql_result driver_mysql_error_result;
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainenstatic const char *mysql_prefix(struct mysql_db *db)
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen{
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen return t_strdup_printf("mysql(%s)", db->host);
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen}
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen
d1c2f6dd39ebb7f6b220ae2afda1162ba72ab43bTimo Sirainenstatic int driver_mysql_connect(struct sql_db *_db)
d1c2f6dd39ebb7f6b220ae2afda1162ba72ab43bTimo Sirainen{
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen struct mysql_db *db = (struct mysql_db *)_db;
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen const char *unix_socket, *host;
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen unsigned long client_flags = db->client_flags;
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen bool failed;
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen i_assert(db->api.state == SQL_DB_STATE_DISCONNECTED);
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen sql_db_set_state(&db->api, SQL_DB_STATE_CONNECTING);
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen if (*db->host == '/') {
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen unix_socket = db->host;
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen host = NULL;
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen } else {
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen unix_socket = NULL;
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen host = db->host;
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen }
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen if (db->option_file != NULL) {
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen mysql_options(db->mysql, MYSQL_READ_DEFAULT_FILE,
7ded22760598b78ee29f9418eacc0abe3fb51055Timo Sirainen db->option_file);
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen }
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen mysql_options(db->mysql, MYSQL_READ_DEFAULT_GROUP,
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen db->option_group != NULL ? db->option_group : "client");
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen if (!db->ssl_set && (db->ssl_ca != NULL || db->ssl_ca_path != NULL)) {
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen#ifdef HAVE_MYSQL_SSL
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen mysql_ssl_set(db->mysql, db->ssl_key, db->ssl_cert,
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen db->ssl_ca, db->ssl_ca_path
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen#ifdef HAVE_MYSQL_SSL_CIPHER
7ded22760598b78ee29f9418eacc0abe3fb51055Timo Sirainen , db->ssl_cipher
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen#endif
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen );
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen db->ssl_set = TRUE;
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen#else
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen i_fatal("mysql: SSL support not compiled in "
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen "(remove ssl_ca and ssl_ca_path settings)");
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen#endif
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen }
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen alarm(SQL_CONNECT_TIMEOUT_SECS);
7ded22760598b78ee29f9418eacc0abe3fb51055Timo Sirainen#ifdef CLIENT_MULTI_RESULTS
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen client_flags |= CLIENT_MULTI_RESULTS;
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen#endif
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen /* CLIENT_MULTI_RESULTS allows the use of stored procedures */
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen failed = mysql_real_connect(db->mysql, host, db->user, db->password,
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen db->dbname, db->port, unix_socket,
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen client_flags) == NULL;
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen alarm(0);
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen if (failed) {
faed8babca9914257f34fb2e603d74016d563b2dTimo Sirainen sql_db_set_state(&db->api, SQL_DB_STATE_DISCONNECTED);
faed8babca9914257f34fb2e603d74016d563b2dTimo Sirainen i_error("%s: Connect failed to database (%s): %s - "
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen "waiting for %u seconds before retry",
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen mysql_prefix(db), db->dbname,
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen mysql_error(db->mysql), db->api.connect_delay);
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen return -1;
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen } else {
7ded22760598b78ee29f9418eacc0abe3fb51055Timo Sirainen i_info("%s: Connected to database %s%s", mysql_prefix(db),
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen db->dbname, db->ssl_set ? " using SSL" : "");
faed8babca9914257f34fb2e603d74016d563b2dTimo Sirainen
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen sql_db_set_state(&db->api, SQL_DB_STATE_IDLE);
faed8babca9914257f34fb2e603d74016d563b2dTimo Sirainen return 1;
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen }
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen}
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainenstatic void driver_mysql_disconnect(struct sql_db *_db ATTR_UNUSED)
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen{
055a42c6987f93fb88291f64813dfceb1b25895dTimo Sirainen}
7ded22760598b78ee29f9418eacc0abe3fb51055Timo Sirainen
7ded22760598b78ee29f9418eacc0abe3fb51055Timo Sirainenstatic void driver_mysql_parse_connect_string(struct mysql_db *db,
7ded22760598b78ee29f9418eacc0abe3fb51055Timo Sirainen const char *connect_string)
faed8babca9914257f34fb2e603d74016d563b2dTimo Sirainen{
7ded22760598b78ee29f9418eacc0abe3fb51055Timo Sirainen const char *const *args, *name, *value;
faed8babca9914257f34fb2e603d74016d563b2dTimo Sirainen const char **field;
faed8babca9914257f34fb2e603d74016d563b2dTimo Sirainen
faed8babca9914257f34fb2e603d74016d563b2dTimo Sirainen db->ssl_cipher = "HIGH";
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen args = t_strsplit_spaces(connect_string, " ");
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen for (; *args != NULL; args++) {
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen value = strchr(*args, '=');
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen if (value == NULL) {
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen i_fatal("mysql: Missing value in connect string: %s",
faed8babca9914257f34fb2e603d74016d563b2dTimo Sirainen *args);
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen }
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen name = t_strdup_until(*args, value);
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen value++;
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen field = NULL;
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen if (strcmp(name, "host") == 0 ||
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen strcmp(name, "hostaddr") == 0)
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen field = &db->host;
faed8babca9914257f34fb2e603d74016d563b2dTimo Sirainen else if (strcmp(name, "user") == 0)
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen field = &db->user;
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen else if (strcmp(name, "password") == 0)
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen field = &db->password;
bf045aa2e9c3846ecde84db303db6588b173bbaeTimo Sirainen else if (strcmp(name, "dbname") == 0)
faed8babca9914257f34fb2e603d74016d563b2dTimo Sirainen field = &db->dbname;
faed8babca9914257f34fb2e603d74016d563b2dTimo Sirainen else if (strcmp(name, "port") == 0)
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen db->port = atoi(value);
faed8babca9914257f34fb2e603d74016d563b2dTimo Sirainen else if (strcmp(name, "client_flags") == 0)
faed8babca9914257f34fb2e603d74016d563b2dTimo Sirainen db->client_flags = atoi(value);
faed8babca9914257f34fb2e603d74016d563b2dTimo Sirainen else if (strcmp(name, "ssl_cert") == 0)
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen field = &db->ssl_cert;
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen else if (strcmp(name, "ssl_key") == 0)
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen field = &db->ssl_key;
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen else if (strcmp(name, "ssl_ca") == 0)
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen field = &db->ssl_ca;
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen else if (strcmp(name, "ssl_ca_path") == 0)
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen field = &db->ssl_ca_path;
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen else if (strcmp(name, "ssl_cipher") == 0)
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen field = &db->ssl_cipher;
7ded22760598b78ee29f9418eacc0abe3fb51055Timo Sirainen else if (strcmp(name, "option_file") == 0)
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen field = &db->option_file;
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen else if (strcmp(name, "option_group") == 0)
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen field = &db->option_group;
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen else
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen i_fatal("mysql: Unknown connect string: %s", name);
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen if (field != NULL)
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen *field = p_strdup(db->pool, value);
7ded22760598b78ee29f9418eacc0abe3fb51055Timo Sirainen }
7ded22760598b78ee29f9418eacc0abe3fb51055Timo Sirainen
7ded22760598b78ee29f9418eacc0abe3fb51055Timo Sirainen if (db->host == NULL)
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen i_fatal("mysql: No hosts given in connect string");
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen db->mysql = mysql_init(NULL);
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen if (db->mysql == NULL)
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen i_fatal("mysql_init() failed");
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen}
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainenstatic struct sql_db *driver_mysql_init_v(const char *connect_string)
faed8babca9914257f34fb2e603d74016d563b2dTimo Sirainen{
190237ce467d2389dfb809874b0fec86d3c7968dTimo Sirainen struct mysql_db *db;
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen pool_t pool;
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen pool = pool_alloconly_create("mysql driver", 1024);
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen db = p_new(pool, struct mysql_db, 1);
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen db->pool = pool;
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen db->api = driver_mysql_db;
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen T_BEGIN {
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen driver_mysql_parse_connect_string(db, connect_string);
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen } T_END;
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen return &db->api;
faed8babca9914257f34fb2e603d74016d563b2dTimo Sirainen}
faed8babca9914257f34fb2e603d74016d563b2dTimo Sirainen
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainenstatic void driver_mysql_deinit_v(struct sql_db *_db)
faed8babca9914257f34fb2e603d74016d563b2dTimo Sirainen{
faed8babca9914257f34fb2e603d74016d563b2dTimo Sirainen struct mysql_db *db = (struct mysql_db *)_db;
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen
88187ee880b4829443e0d55ea7d145d9d5880217Timo Sirainen _db->no_reconnect = TRUE;
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen sql_db_set_state(&db->api, SQL_DB_STATE_DISCONNECTED);
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen mysql_close(db->mysql);
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen array_free(&_db->module_contexts);
41bb0aa8e357876bc9a1916a37c9e3e78e5f8185Timo Sirainen pool_unref(&db->pool);
faed8babca9914257f34fb2e603d74016d563b2dTimo Sirainen}
faed8babca9914257f34fb2e603d74016d563b2dTimo Sirainen
faed8babca9914257f34fb2e603d74016d563b2dTimo Sirainenstatic int driver_mysql_do_query(struct mysql_db *db, const char *query)
faed8babca9914257f34fb2e603d74016d563b2dTimo Sirainen{
faed8babca9914257f34fb2e603d74016d563b2dTimo Sirainen if (mysql_query(db->mysql, query) == 0)
faed8babca9914257f34fb2e603d74016d563b2dTimo Sirainen return 0;
8f7b00599e73fe71b1d2c6c65f8ae98aac1b23fbTimo Sirainen
8f7b00599e73fe71b1d2c6c65f8ae98aac1b23fbTimo Sirainen /* failed */
faed8babca9914257f34fb2e603d74016d563b2dTimo Sirainen switch (mysql_errno(db->mysql)) {
faed8babca9914257f34fb2e603d74016d563b2dTimo Sirainen case CR_SERVER_GONE_ERROR:
faed8babca9914257f34fb2e603d74016d563b2dTimo Sirainen case CR_SERVER_LOST:
faed8babca9914257f34fb2e603d74016d563b2dTimo Sirainen sql_db_set_state(&db->api, SQL_DB_STATE_DISCONNECTED);
faed8babca9914257f34fb2e603d74016d563b2dTimo Sirainen break;
faed8babca9914257f34fb2e603d74016d563b2dTimo Sirainen default:
faed8babca9914257f34fb2e603d74016d563b2dTimo Sirainen break;
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen }
faed8babca9914257f34fb2e603d74016d563b2dTimo Sirainen return -1;
faed8babca9914257f34fb2e603d74016d563b2dTimo Sirainen}
faed8babca9914257f34fb2e603d74016d563b2dTimo Sirainen
faed8babca9914257f34fb2e603d74016d563b2dTimo Sirainenstatic const char *
faed8babca9914257f34fb2e603d74016d563b2dTimo Sirainendriver_mysql_escape_string(struct sql_db *_db, const char *string)
faed8babca9914257f34fb2e603d74016d563b2dTimo Sirainen{
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen struct mysql_db *db = (struct mysql_db *)_db;
faed8babca9914257f34fb2e603d74016d563b2dTimo Sirainen size_t len = strlen(string);
faed8babca9914257f34fb2e603d74016d563b2dTimo Sirainen char *to;
faed8babca9914257f34fb2e603d74016d563b2dTimo Sirainen
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen if (_db->state == SQL_DB_STATE_DISCONNECTED) {
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen /* try connecting */
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen (void)sql_connect(&db->api);
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen }
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen if (db->mysql == NULL) {
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen /* FIXME: we don't have a valid connection, so fallback
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen to using default escaping. the next query will most
faed8babca9914257f34fb2e603d74016d563b2dTimo Sirainen likely fail anyway so it shouldn't matter that much
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen what we return here.. Anyway, this API needs
faed8babca9914257f34fb2e603d74016d563b2dTimo Sirainen changing so that the escaping function could already
7ded22760598b78ee29f9418eacc0abe3fb51055Timo Sirainen fail the query reliably. */
faed8babca9914257f34fb2e603d74016d563b2dTimo Sirainen to = t_buffer_get(len * 2 + 1);
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen len = mysql_escape_string(to, string, len);
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen t_buffer_alloc(len + 1);
faed8babca9914257f34fb2e603d74016d563b2dTimo Sirainen return to;
faed8babca9914257f34fb2e603d74016d563b2dTimo Sirainen }
faed8babca9914257f34fb2e603d74016d563b2dTimo Sirainen
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen to = t_buffer_get(len * 2 + 1);
faed8babca9914257f34fb2e603d74016d563b2dTimo Sirainen len = mysql_real_escape_string(db->mysql, to, string, len);
faed8babca9914257f34fb2e603d74016d563b2dTimo Sirainen t_buffer_alloc(len + 1);
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen return to;
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen}
faed8babca9914257f34fb2e603d74016d563b2dTimo Sirainen
8870854fbd44602a2d2174177c305f3570401a09Timo Sirainenstatic void driver_mysql_exec(struct sql_db *_db, const char *query)
8870854fbd44602a2d2174177c305f3570401a09Timo Sirainen{
8870854fbd44602a2d2174177c305f3570401a09Timo Sirainen struct mysql_db *db = (struct mysql_db *)_db;
8870854fbd44602a2d2174177c305f3570401a09Timo Sirainen
8870854fbd44602a2d2174177c305f3570401a09Timo Sirainen if (driver_mysql_do_query(db, query) < 0) {
faed8babca9914257f34fb2e603d74016d563b2dTimo Sirainen i_error("%s: Query '%s' failed: %s",
faed8babca9914257f34fb2e603d74016d563b2dTimo Sirainen mysql_prefix(db), query, mysql_error(db->mysql));
faed8babca9914257f34fb2e603d74016d563b2dTimo Sirainen }
faed8babca9914257f34fb2e603d74016d563b2dTimo Sirainen}
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainenstatic void driver_mysql_query(struct sql_db *db, const char *query,
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen sql_query_callback_t *callback, void *context)
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen{
7ded22760598b78ee29f9418eacc0abe3fb51055Timo Sirainen struct sql_result *result;
7ded22760598b78ee29f9418eacc0abe3fb51055Timo Sirainen
7ded22760598b78ee29f9418eacc0abe3fb51055Timo Sirainen result = sql_query_s(db, query);
7ded22760598b78ee29f9418eacc0abe3fb51055Timo Sirainen result->callback = TRUE;
7ded22760598b78ee29f9418eacc0abe3fb51055Timo Sirainen callback(result, context);
f0559b6bd1914e9118772d49415defe3dbb1817bTimo Sirainen result->callback = FALSE;
41e1c7380edda701719d8ce1fb4d465d2ec4c84dTimo Sirainen sql_result_unref(result);
f0559b6bd1914e9118772d49415defe3dbb1817bTimo Sirainen}
f0559b6bd1914e9118772d49415defe3dbb1817bTimo Sirainen
f0559b6bd1914e9118772d49415defe3dbb1817bTimo Sirainenstatic struct sql_result *
f0559b6bd1914e9118772d49415defe3dbb1817bTimo Sirainendriver_mysql_query_s(struct sql_db *_db, const char *query)
41e1c7380edda701719d8ce1fb4d465d2ec4c84dTimo Sirainen{
41e1c7380edda701719d8ce1fb4d465d2ec4c84dTimo Sirainen struct mysql_db *db = (struct mysql_db *)_db;
41e1c7380edda701719d8ce1fb4d465d2ec4c84dTimo Sirainen struct mysql_result *result;
190237ce467d2389dfb809874b0fec86d3c7968dTimo Sirainen int ret;
b0be0bead3d6963149f7f2a9504b8ab5aced9af5Timo Sirainen
41e1c7380edda701719d8ce1fb4d465d2ec4c84dTimo Sirainen result = i_new(struct mysql_result, 1);
41e1c7380edda701719d8ce1fb4d465d2ec4c84dTimo Sirainen result->api = driver_mysql_result;
41e1c7380edda701719d8ce1fb4d465d2ec4c84dTimo Sirainen
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen if (driver_mysql_do_query(db, query) < 0)
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen result->api = driver_mysql_error_result;
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen else {
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen /* query ok */
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen result->affected_rows = mysql_affected_rows(db->mysql);
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen result->result = mysql_store_result(db->mysql);
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen#ifdef CLIENT_MULTI_RESULTS
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen /* Because we've enabled CLIENT_MULTI_RESULTS, we need to read
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen (ignore) extra results - there should not be any.
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen ret is: -1 = done, >0 = error, 0 = more results. */
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen while ((ret = mysql_next_result(db->mysql)) == 0) ;
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen#else
faed8babca9914257f34fb2e603d74016d563b2dTimo Sirainen ret = -1;
faed8babca9914257f34fb2e603d74016d563b2dTimo Sirainen#endif
faed8babca9914257f34fb2e603d74016d563b2dTimo Sirainen
faed8babca9914257f34fb2e603d74016d563b2dTimo Sirainen if (ret < 0 &&
faed8babca9914257f34fb2e603d74016d563b2dTimo Sirainen (result->result != NULL || mysql_errno(db->mysql) == 0)) {
faed8babca9914257f34fb2e603d74016d563b2dTimo Sirainen /* ok */
faed8babca9914257f34fb2e603d74016d563b2dTimo Sirainen } else {
faed8babca9914257f34fb2e603d74016d563b2dTimo Sirainen /* failed */
faed8babca9914257f34fb2e603d74016d563b2dTimo Sirainen if (result->result != NULL)
faed8babca9914257f34fb2e603d74016d563b2dTimo Sirainen mysql_free_result(result->result);
7ded22760598b78ee29f9418eacc0abe3fb51055Timo Sirainen result->api = driver_mysql_error_result;
faed8babca9914257f34fb2e603d74016d563b2dTimo Sirainen }
7ded22760598b78ee29f9418eacc0abe3fb51055Timo Sirainen }
faed8babca9914257f34fb2e603d74016d563b2dTimo Sirainen
faed8babca9914257f34fb2e603d74016d563b2dTimo Sirainen result->api.db = _db;
faed8babca9914257f34fb2e603d74016d563b2dTimo Sirainen result->api.refcount = 1;
faed8babca9914257f34fb2e603d74016d563b2dTimo Sirainen return &result->api;
7ded22760598b78ee29f9418eacc0abe3fb51055Timo Sirainen}
faed8babca9914257f34fb2e603d74016d563b2dTimo Sirainen
faed8babca9914257f34fb2e603d74016d563b2dTimo Sirainenstatic void driver_mysql_result_free(struct sql_result *_result)
88187ee880b4829443e0d55ea7d145d9d5880217Timo Sirainen{
88187ee880b4829443e0d55ea7d145d9d5880217Timo Sirainen struct mysql_result *result = (struct mysql_result *)_result;
faed8babca9914257f34fb2e603d74016d563b2dTimo Sirainen
faed8babca9914257f34fb2e603d74016d563b2dTimo Sirainen i_assert(_result != &sql_not_connected_result);
faed8babca9914257f34fb2e603d74016d563b2dTimo Sirainen if (_result->callback)
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen return;
faed8babca9914257f34fb2e603d74016d563b2dTimo Sirainen
faed8babca9914257f34fb2e603d74016d563b2dTimo Sirainen if (result->result != NULL)
faed8babca9914257f34fb2e603d74016d563b2dTimo Sirainen mysql_free_result(result->result);
faed8babca9914257f34fb2e603d74016d563b2dTimo Sirainen i_free(result);
faed8babca9914257f34fb2e603d74016d563b2dTimo Sirainen}
faed8babca9914257f34fb2e603d74016d563b2dTimo Sirainen
faed8babca9914257f34fb2e603d74016d563b2dTimo Sirainenstatic int driver_mysql_result_next_row(struct sql_result *_result)
faed8babca9914257f34fb2e603d74016d563b2dTimo Sirainen{
struct mysql_result *result = (struct mysql_result *)_result;
struct mysql_db *db = (struct mysql_db *)_result->db;
if (result->result == NULL) {
/* no results */
return 0;
}
result->row = mysql_fetch_row(result->result);
if (result->row != NULL)
return 1;
return mysql_errno(db->mysql) != 0 ? -1 : 0;
}
static void driver_mysql_result_fetch_fields(struct mysql_result *result)
{
if (result->fields != NULL)
return;
result->fields_count = mysql_num_fields(result->result);
result->fields = mysql_fetch_fields(result->result);
}
static unsigned int
driver_mysql_result_get_fields_count(struct sql_result *_result)
{
struct mysql_result *result = (struct mysql_result *)_result;
driver_mysql_result_fetch_fields(result);
return result->fields_count;
}
static const char *
driver_mysql_result_get_field_name(struct sql_result *_result, unsigned int idx)
{
struct mysql_result *result = (struct mysql_result *)_result;
driver_mysql_result_fetch_fields(result);
i_assert(idx < result->fields_count);
return result->fields[idx].name;
}
static int driver_mysql_result_find_field(struct sql_result *_result,
const char *field_name)
{
struct mysql_result *result = (struct mysql_result *)_result;
unsigned int i;
driver_mysql_result_fetch_fields(result);
for (i = 0; i < result->fields_count; i++) {
if (strcmp(result->fields[i].name, field_name) == 0)
return i;
}
return -1;
}
static const char *
driver_mysql_result_get_field_value(struct sql_result *_result,
unsigned int idx)
{
struct mysql_result *result = (struct mysql_result *)_result;
return (const char *)result->row[idx];
}
static const unsigned char *
driver_mysql_result_get_field_value_binary(struct sql_result *_result,
unsigned int idx, size_t *size_r)
{
struct mysql_result *result = (struct mysql_result *)_result;
unsigned long *lengths;
lengths = mysql_fetch_lengths(result->result);
*size_r = lengths[idx];
return (const void *)result->row[idx];
}
static const char *
driver_mysql_result_find_field_value(struct sql_result *result,
const char *field_name)
{
int idx;
idx = driver_mysql_result_find_field(result, field_name);
if (idx < 0)
return NULL;
return driver_mysql_result_get_field_value(result, idx);
}
static const char *const *
driver_mysql_result_get_values(struct sql_result *_result)
{
struct mysql_result *result = (struct mysql_result *)_result;
return (const char *const *)result->row;
}
static const char *driver_mysql_result_get_error(struct sql_result *_result)
{
struct mysql_db *db = (struct mysql_db *)_result->db;
return mysql_error(db->mysql);
}
static struct sql_transaction_context *
driver_mysql_transaction_begin(struct sql_db *db)
{
struct mysql_transaction_context *ctx;
ctx = i_new(struct mysql_transaction_context, 1);
ctx->ctx.db = db;
ctx->query_pool = pool_alloconly_create("mysql transaction", 1024);
return &ctx->ctx;
}
static void
driver_mysql_transaction_commit(struct sql_transaction_context *ctx,
sql_commit_callback_t *callback, void *context)
{
const char *error;
if (sql_transaction_commit_s(&ctx, &error) < 0)
callback(error, context);
else
callback(NULL, context);
}
static int
transaction_send_query(struct mysql_transaction_context *ctx, const char *query,
unsigned int *affected_rows_r)
{
struct sql_result *_result;
int ret = 0;
if (ctx->failed)
return -1;
_result = sql_query_s(ctx->ctx.db, query);
if (sql_result_next_row(_result) < 0) {
ctx->error = sql_result_get_error(_result);
ctx->failed = TRUE;
ret = -1;
} else if (affected_rows_r != NULL) {
struct mysql_result *result = (struct mysql_result *)_result;
i_assert(result->affected_rows != (my_ulonglong)-1);
*affected_rows_r = result->affected_rows;
}
sql_result_unref(_result);
return ret;
}
static int
driver_mysql_transaction_commit_s(struct sql_transaction_context *_ctx,
const char **error_r)
{
struct mysql_transaction_context *ctx =
(struct mysql_transaction_context *)_ctx;
int ret = 0;
*error_r = NULL;
if (_ctx->head != NULL) {
/* try to use a transaction in any case,
even if it doesn't work. */
(void)transaction_send_query(ctx, "BEGIN", NULL);
while (_ctx->head != NULL) {
if (transaction_send_query(ctx, _ctx->head->query,
_ctx->head->affected_rows) < 0)
break;
_ctx->head = _ctx->head->next;
}
ret = transaction_send_query(ctx, "COMMIT", NULL);
*error_r = ctx->error;
}
sql_transaction_rollback(&_ctx);
return ret;
}
static void
driver_mysql_transaction_rollback(struct sql_transaction_context *_ctx)
{
struct mysql_transaction_context *ctx =
(struct mysql_transaction_context *)_ctx;
pool_unref(&ctx->query_pool);
i_free(ctx);
}
static void
driver_mysql_update(struct sql_transaction_context *_ctx, const char *query,
unsigned int *affected_rows)
{
struct mysql_transaction_context *ctx =
(struct mysql_transaction_context *)_ctx;
sql_transaction_add_query(&ctx->ctx, ctx->query_pool,
query, affected_rows);
}
const struct sql_db driver_mysql_db = {
.name = "mysql",
.flags = SQL_DB_FLAG_BLOCKING | SQL_DB_FLAG_POOLED,
.v = {
driver_mysql_init_v,
driver_mysql_deinit_v,
driver_mysql_connect,
driver_mysql_disconnect,
driver_mysql_escape_string,
driver_mysql_exec,
driver_mysql_query,
driver_mysql_query_s,
driver_mysql_transaction_begin,
driver_mysql_transaction_commit,
driver_mysql_transaction_commit_s,
driver_mysql_transaction_rollback,
driver_mysql_update
}
};
const struct sql_result driver_mysql_result = {
.v = {
driver_mysql_result_free,
driver_mysql_result_next_row,
driver_mysql_result_get_fields_count,
driver_mysql_result_get_field_name,
driver_mysql_result_find_field,
driver_mysql_result_get_field_value,
driver_mysql_result_get_field_value_binary,
driver_mysql_result_find_field_value,
driver_mysql_result_get_values,
driver_mysql_result_get_error
}
};
static int
driver_mysql_result_error_next_row(struct sql_result *result ATTR_UNUSED)
{
return -1;
}
const struct sql_result driver_mysql_error_result = {
.v = {
driver_mysql_result_free,
driver_mysql_result_error_next_row,
NULL, NULL, NULL, NULL, NULL, NULL, NULL,
driver_mysql_result_get_error
},
.failed_try_retry = TRUE
};
const char *driver_mysql_version = DOVECOT_VERSION;
void driver_mysql_init(void);
void driver_mysql_deinit(void);
void driver_mysql_init(void)
{
sql_driver_register(&driver_mysql_db);
}
void driver_mysql_deinit(void)
{
sql_driver_unregister(&driver_mysql_db);
}
#endif