driver-mysql.c revision d13a8e21656e1b7005e094f7b9c9e3611105c648
183bea41fa640dc8117f3eb45ff935cd81377a84Timo Sirainen/* Copyright (c) 2003-2012 Dovecot authors, see the included COPYING file */
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen/* ugly way to tell clang that mysql.h is a system header and we don't want
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen to enable nonnull attributes for it by default.. */
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen const char *user, *password, *dbname, *host, *unix_socket;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen const char *ssl_cert, *ssl_key, *ssl_ca, *ssl_ca_path, *ssl_cipher;
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainenextern const struct sql_result driver_mysql_result;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainenextern const struct sql_result driver_mysql_error_result;
f635aa2fd6c47ec297cf68981203a8dc7f8a23c3Timo Sirainenstatic const char *mysql_prefix(struct mysql_db *db)
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen return t_strdup_printf("mysql(%s)", db->host);
b57cd9928909b51fa473c3eea81442e296006438Timo Sirainenstatic int driver_mysql_connect(struct sql_db *_db)
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen unsigned long client_flags = db->client_flags;
6bca3405636e3ec95724350c3a10d6fcb737782aTimo Sirainen i_assert(db->api.state == SQL_DB_STATE_DISCONNECTED);
57ca8cc86193103127066c724815e7e7a24926dbTimo Sirainen sql_db_set_state(&db->api, SQL_DB_STATE_CONNECTING);
6bca3405636e3ec95724350c3a10d6fcb737782aTimo Sirainen mysql_options(db->mysql, MYSQL_READ_DEFAULT_FILE,
6bca3405636e3ec95724350c3a10d6fcb737782aTimo Sirainen mysql_options(db->mysql, MYSQL_READ_DEFAULT_GROUP,
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen db->option_group != NULL ? db->option_group : "client");
6bca3405636e3ec95724350c3a10d6fcb737782aTimo Sirainen if (!db->ssl_set && (db->ssl_ca != NULL || db->ssl_ca_path != NULL)) {
6bca3405636e3ec95724350c3a10d6fcb737782aTimo Sirainen mysql_ssl_set(db->mysql, db->ssl_key, db->ssl_cert,
06b0c3be9905099038964b068216bbed155701deTimo Sirainen "(remove ssl_ca and ssl_ca_path settings)");
685a84a0ccb55cb9359dcd57d9eb3b6b836034a2Timo Sirainen /* CLIENT_MULTI_RESULTS allows the use of stored procedures */
f635aa2fd6c47ec297cf68981203a8dc7f8a23c3Timo Sirainen failed = mysql_real_connect(db->mysql, host, db->user, db->password,
5fbf8719b9ef072295c16bc4492f9f0ece92117dTimo Sirainen secs_used = SQL_CONNECT_TIMEOUT_SECS - alarm(0);
5fbf8719b9ef072295c16bc4492f9f0ece92117dTimo Sirainen /* connecting could have taken a while. make sure that any
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen timeouts that get added soon will get a refreshed
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen timestamp. */
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen sql_db_set_state(&db->api, SQL_DB_STATE_DISCONNECTED);
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen i_error("%s: Connect failed to database (%s): %s - "
a0b89f3b1df99b3a32f44623f13ad1893118825bTimo Sirainen "waiting for %u seconds before retry",
6bca3405636e3ec95724350c3a10d6fcb737782aTimo Sirainen mysql_error(db->mysql), db->api.connect_delay);
6bca3405636e3ec95724350c3a10d6fcb737782aTimo Sirainen i_info("%s: Connected to database %s%s", mysql_prefix(db),
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen sql_db_set_state(&db->api, SQL_DB_STATE_IDLE);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainenstatic void driver_mysql_disconnect(struct sql_db *_db ATTR_UNUSED)
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainenstatic void driver_mysql_parse_connect_string(struct mysql_db *db,
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen const char **field;
6bca3405636e3ec95724350c3a10d6fcb737782aTimo Sirainen args = t_strsplit_spaces(connect_string, " ");
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen i_fatal("mysql: Missing value in connect string: %s",
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen i_fatal("mysql: Unknown connect string: %s", name);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen i_fatal("mysql: No hosts given in connect string");
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainenstatic struct sql_db *driver_mysql_init_v(const char *connect_string)
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen pool = pool_alloconly_create("mysql driver", 1024);
9222c96ab9f42b25d5d5260f9e7a42c694715b0aTimo Sirainen driver_mysql_parse_connect_string(db, connect_string);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainenstatic void driver_mysql_deinit_v(struct sql_db *_db)
6bca3405636e3ec95724350c3a10d6fcb737782aTimo Sirainen sql_db_set_state(&db->api, SQL_DB_STATE_DISCONNECTED);
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainenstatic int driver_mysql_do_query(struct mysql_db *db, const char *query)
24ce0c343cefe54af841871fa39dbc3464028b06Timo Sirainen sql_db_set_state(&db->api, SQL_DB_STATE_DISCONNECTED);
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainenstatic const char *
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainendriver_mysql_escape_string(struct sql_db *_db, const char *string)
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen if (_db->state == SQL_DB_STATE_DISCONNECTED) {
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen /* try connecting */
24ce0c343cefe54af841871fa39dbc3464028b06Timo Sirainen /* FIXME: we don't have a valid connection, so fallback
24ce0c343cefe54af841871fa39dbc3464028b06Timo Sirainen to using default escaping. the next query will most
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen likely fail anyway so it shouldn't matter that much
24ce0c343cefe54af841871fa39dbc3464028b06Timo Sirainen what we return here.. Anyway, this API needs
24ce0c343cefe54af841871fa39dbc3464028b06Timo Sirainen changing so that the escaping function could already
24ce0c343cefe54af841871fa39dbc3464028b06Timo Sirainen fail the query reliably. */
5fbf8719b9ef072295c16bc4492f9f0ece92117dTimo Sirainen len = mysql_real_escape_string(db->mysql, to, string, len);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainenstatic void driver_mysql_exec(struct sql_db *_db, const char *query)
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen mysql_prefix(db), query, mysql_error(db->mysql));
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainenstatic void driver_mysql_query(struct sql_db *db, const char *query,
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen sql_query_callback_t *callback, void *context)
a5be5a0d074ed0f3e4106612a2792e143a43efc6Timo Sirainenstatic struct sql_result *
a5be5a0d074ed0f3e4106612a2792e143a43efc6Timo Sirainendriver_mysql_query_s(struct sql_db *_db, const char *query)
e8fd206cf9dca263278efba21864606126fc29b8Timo Sirainen /* query ok */
e8fd206cf9dca263278efba21864606126fc29b8Timo Sirainen result->affected_rows = mysql_affected_rows(db->mysql);
a5be5a0d074ed0f3e4106612a2792e143a43efc6Timo Sirainen result->result = mysql_store_result(db->mysql);
a5be5a0d074ed0f3e4106612a2792e143a43efc6Timo Sirainen /* Because we've enabled CLIENT_MULTI_RESULTS, we need to read
a5be5a0d074ed0f3e4106612a2792e143a43efc6Timo Sirainen (ignore) extra results - there should not be any.
a5be5a0d074ed0f3e4106612a2792e143a43efc6Timo Sirainen ret is: -1 = done, >0 = error, 0 = more results. */
a5be5a0d074ed0f3e4106612a2792e143a43efc6Timo Sirainen while ((ret = mysql_next_result(db->mysql)) == 0) ;
3656c91dcb8336814bebd4500e81c3dde25233e6Timo Sirainen (result->result != NULL || mysql_errno(db->mysql) == 0)) {
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainenstatic void driver_mysql_result_free(struct sql_result *_result)
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen struct mysql_result *result = (struct mysql_result *)_result;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen i_assert(_result != &sql_not_connected_result);
a2550844936da8b78d7565b905a4dc5ffb3eef0eTimo Sirainenstatic int driver_mysql_result_next_row(struct sql_result *_result)
a2550844936da8b78d7565b905a4dc5ffb3eef0eTimo Sirainen struct mysql_result *result = (struct mysql_result *)_result;
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen struct mysql_db *db = (struct mysql_db *)_result->db;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen /* no results */
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen result->row = mysql_fetch_row(result->result);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainenstatic void driver_mysql_result_fetch_fields(struct mysql_result *result)
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen result->fields_count = mysql_num_fields(result->result);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen result->fields = mysql_fetch_fields(result->result);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainenstatic unsigned int
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainendriver_mysql_result_get_fields_count(struct sql_result *_result)
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen struct mysql_result *result = (struct mysql_result *)_result;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainenstatic const char *
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainendriver_mysql_result_get_field_name(struct sql_result *_result, unsigned int idx)
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen struct mysql_result *result = (struct mysql_result *)_result;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainenstatic int driver_mysql_result_find_field(struct sql_result *_result,
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen struct mysql_result *result = (struct mysql_result *)_result;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen unsigned int i;
b3f46fa8a890527996fd0288cf964b7f3567cd22Timo Sirainen if (strcmp(result->fields[i].name, field_name) == 0)
b3f46fa8a890527996fd0288cf964b7f3567cd22Timo Sirainenstatic const char *
b3f46fa8a890527996fd0288cf964b7f3567cd22Timo Sirainendriver_mysql_result_get_field_value(struct sql_result *_result,
b3f46fa8a890527996fd0288cf964b7f3567cd22Timo Sirainen unsigned int idx)
1ac19c5c2b66a12f5598792aad15114ee3eb62e2Timo Sirainen struct mysql_result *result = (struct mysql_result *)_result;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainenstatic const unsigned char *
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainendriver_mysql_result_get_field_value_binary(struct sql_result *_result,
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen struct mysql_result *result = (struct mysql_result *)_result;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen unsigned long *lengths;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen lengths = mysql_fetch_lengths(result->result);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainenstatic const char *
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainendriver_mysql_result_find_field_value(struct sql_result *result,
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen idx = driver_mysql_result_find_field(result, field_name);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen return driver_mysql_result_get_field_value(result, idx);
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainenstatic const char *const *
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainendriver_mysql_result_get_values(struct sql_result *_result)
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen struct mysql_result *result = (struct mysql_result *)_result;
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainenstatic const char *driver_mysql_result_get_error(struct sql_result *_result)
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen struct mysql_db *db = (struct mysql_db *)_result->db;
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen if ((err == CR_SERVER_GONE_ERROR || err == CR_SERVER_LOST) &&
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen errstr = t_strdup_printf("%s (idled for %u secs)",
7d075009a641d88a45940238676883a8eaf1507bTimo Sirainendriver_mysql_transaction_begin(struct sql_db *db)
f1ea65eb88b4f67e572c4640882bdf00fe3a2186Timo Sirainen ctx = i_new(struct mysql_transaction_context, 1);
f1ea65eb88b4f67e572c4640882bdf00fe3a2186Timo Sirainen ctx->query_pool = pool_alloconly_create("mysql transaction", 1024);
bfdf0fd7b6186f64cbdcbf1cb2bf9c42a9007b77Timo Sirainendriver_mysql_transaction_commit(struct sql_transaction_context *ctx,
f1ea65eb88b4f67e572c4640882bdf00fe3a2186Timo Sirainen sql_commit_callback_t *callback, void *context)
7d075009a641d88a45940238676883a8eaf1507bTimo Sirainen if (sql_transaction_commit_s(&ctx, &error) < 0)
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainentransaction_send_query(struct mysql_transaction_context *ctx, const char *query,
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen struct mysql_result *result = (struct mysql_result *)_result;
7d075009a641d88a45940238676883a8eaf1507bTimo Sirainen i_assert(result->affected_rows != (my_ulonglong)-1);
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainenstatic int driver_mysql_try_commit_s(struct mysql_transaction_context *ctx)
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen struct sql_transaction_context *_ctx = &ctx->ctx;
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen /* try to use a transaction in any case,
648d24583c1574441c4fa0331a90bd4d6e7996c5Timo Sirainen even if it's not actually functional. */
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen if (transaction_send_query(ctx, "BEGIN", NULL) < 0) {
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen if (_ctx->db->state != SQL_DB_STATE_DISCONNECTED)
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen /* we got disconnected, retry */
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen if (transaction_send_query(ctx, _ctx->head->query,
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen if (transaction_send_query(ctx, "COMMIT", NULL) < 0)
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainendriver_mysql_transaction_commit_s(struct sql_transaction_context *_ctx,
0ae010139a1bb3b29fbf117c5da1a6a6c6b7b5a0Timo Sirainen const char **error_r)
0ae010139a1bb3b29fbf117c5da1a6a6c6b7b5a0Timo Sirainen struct mysql_db *db = (struct mysql_db *)_ctx->db;
1ac19c5c2b66a12f5598792aad15114ee3eb62e2Timo Sirainendriver_mysql_transaction_rollback(struct sql_transaction_context *_ctx)
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainendriver_mysql_update(struct sql_transaction_context *_ctx, const char *query,
1ac19c5c2b66a12f5598792aad15114ee3eb62e2Timo Sirainen sql_transaction_add_query(&ctx->ctx, ctx->query_pool,
0371406d952fe51367c7be91703e5634b7d9d225Timo Sirainen .flags = SQL_DB_FLAG_BLOCKING | SQL_DB_FLAG_POOLED,
void driver_mysql_init(void);
void driver_mysql_deinit(void);
void driver_mysql_init(void)
void driver_mysql_deinit(void)