driver-mysql.c revision 6920cf9a4d7bb8fd1a5b5bd25fafc8dd0cb9817c
6463ed1dcdd45416468b3fa178bd856b5a9ed2c3Jakub Hrozek/* Copyright (c) 2003-2015 Dovecot authors, see the included COPYING file */
a7797068c4deb6ce2bdbcda27c45ff1bbb4a8e78Jakub Hrozek/* ugly way to tell clang that mysql.h is a system header and we don't want
6463ed1dcdd45416468b3fa178bd856b5a9ed2c3Jakub Hrozek to enable nonnull attributes for it by default.. */
6463ed1dcdd45416468b3fa178bd856b5a9ed2c3Jakub Hrozek const char *user, *password, *dbname, *host, *unix_socket;
6463ed1dcdd45416468b3fa178bd856b5a9ed2c3Jakub Hrozek const char *ssl_cert, *ssl_key, *ssl_ca, *ssl_ca_path, *ssl_cipher;
6463ed1dcdd45416468b3fa178bd856b5a9ed2c3Jakub Hrozek unsigned int connect_timeout, read_timeout, write_timeout;
a23014d69b56cbdf48ad05229c334648b5309d8fJakub Hrozekextern const struct sql_result driver_mysql_result;
6463ed1dcdd45416468b3fa178bd856b5a9ed2c3Jakub Hrozekextern const struct sql_result driver_mysql_error_result;
6463ed1dcdd45416468b3fa178bd856b5a9ed2c3Jakub Hrozekstatic const char *mysql_prefix(struct mysql_db *db)
6463ed1dcdd45416468b3fa178bd856b5a9ed2c3Jakub Hrozekstatic int driver_mysql_connect(struct sql_db *_db)
6463ed1dcdd45416468b3fa178bd856b5a9ed2c3Jakub Hrozek i_assert(db->api.state == SQL_DB_STATE_DISCONNECTED);
6463ed1dcdd45416468b3fa178bd856b5a9ed2c3Jakub Hrozek sql_db_set_state(&db->api, SQL_DB_STATE_CONNECTING);
6463ed1dcdd45416468b3fa178bd856b5a9ed2c3Jakub Hrozek /* assume option_file overrides the host, or if not we'll just
6463ed1dcdd45416468b3fa178bd856b5a9ed2c3Jakub Hrozek connect to localhost */
6463ed1dcdd45416468b3fa178bd856b5a9ed2c3Jakub Hrozek mysql_options(db->mysql, MYSQL_READ_DEFAULT_FILE,
6463ed1dcdd45416468b3fa178bd856b5a9ed2c3Jakub Hrozek mysql_options(db->mysql, MYSQL_OPT_CONNECT_TIMEOUT, &db->connect_timeout);
6463ed1dcdd45416468b3fa178bd856b5a9ed2c3Jakub Hrozek mysql_options(db->mysql, MYSQL_OPT_READ_TIMEOUT, &db->read_timeout);
6463ed1dcdd45416468b3fa178bd856b5a9ed2c3Jakub Hrozek mysql_options(db->mysql, MYSQL_OPT_WRITE_TIMEOUT, &db->write_timeout);
6463ed1dcdd45416468b3fa178bd856b5a9ed2c3Jakub Hrozek mysql_options(db->mysql, MYSQL_READ_DEFAULT_GROUP,
6463ed1dcdd45416468b3fa178bd856b5a9ed2c3Jakub Hrozek db->option_group != NULL ? db->option_group : "client");
6463ed1dcdd45416468b3fa178bd856b5a9ed2c3Jakub Hrozek if (!db->ssl_set && (db->ssl_ca != NULL || db->ssl_ca_path != NULL)) {
6463ed1dcdd45416468b3fa178bd856b5a9ed2c3Jakub Hrozek mysql_ssl_set(db->mysql, db->ssl_key, db->ssl_cert,
6463ed1dcdd45416468b3fa178bd856b5a9ed2c3Jakub Hrozek mysql_options(db->mysql, MYSQL_OPT_SSL_VERIFY_SERVER_CERT,
6463ed1dcdd45416468b3fa178bd856b5a9ed2c3Jakub Hrozek "(remove ssl_ca and ssl_ca_path settings)");
6463ed1dcdd45416468b3fa178bd856b5a9ed2c3Jakub Hrozek /* CLIENT_MULTI_RESULTS allows the use of stored procedures */
a23014d69b56cbdf48ad05229c334648b5309d8fJakub Hrozek failed = mysql_real_connect(db->mysql, host, db->user, db->password,
6463ed1dcdd45416468b3fa178bd856b5a9ed2c3Jakub Hrozek /* connecting could have taken a while. make sure that any
6463ed1dcdd45416468b3fa178bd856b5a9ed2c3Jakub Hrozek timeouts that get added soon will get a refreshed
6463ed1dcdd45416468b3fa178bd856b5a9ed2c3Jakub Hrozek timestamp. */
6463ed1dcdd45416468b3fa178bd856b5a9ed2c3Jakub Hrozek sql_db_set_state(&db->api, SQL_DB_STATE_DISCONNECTED);
6463ed1dcdd45416468b3fa178bd856b5a9ed2c3Jakub Hrozek i_error("%s: Connect failed to database (%s): %s - "
6463ed1dcdd45416468b3fa178bd856b5a9ed2c3Jakub Hrozek "waiting for %u seconds before retry",
6463ed1dcdd45416468b3fa178bd856b5a9ed2c3Jakub Hrozek mysql_error(db->mysql), db->api.connect_delay);
6463ed1dcdd45416468b3fa178bd856b5a9ed2c3Jakub Hrozekstatic void driver_mysql_disconnect(struct sql_db *_db ATTR_UNUSED)
6463ed1dcdd45416468b3fa178bd856b5a9ed2c3Jakub Hrozekstatic void driver_mysql_parse_connect_string(struct mysql_db *db,
6463ed1dcdd45416468b3fa178bd856b5a9ed2c3Jakub Hrozek const char **field;
6463ed1dcdd45416468b3fa178bd856b5a9ed2c3Jakub Hrozek db->ssl_verify_server_cert = 0; /* FIXME: change to 1 for v2.3 */
6463ed1dcdd45416468b3fa178bd856b5a9ed2c3Jakub Hrozek db->connect_timeout = SQL_CONNECT_TIMEOUT_SECS;
6463ed1dcdd45416468b3fa178bd856b5a9ed2c3Jakub Hrozek db->read_timeout = MYSQL_DEFAULT_READ_TIMEOUT_SECS;
6463ed1dcdd45416468b3fa178bd856b5a9ed2c3Jakub Hrozek db->write_timeout = MYSQL_DEFAULT_WRITE_TIMEOUT_SECS;
6463ed1dcdd45416468b3fa178bd856b5a9ed2c3Jakub Hrozek i_fatal("mysql: Missing value in connect string: %s",
a23014d69b56cbdf48ad05229c334648b5309d8fJakub Hrozek i_fatal("mysql: Invalid port number: %s", value);
a23014d69b56cbdf48ad05229c334648b5309d8fJakub Hrozek } else if (strcmp(name, "client_flags") == 0) {
a23014d69b56cbdf48ad05229c334648b5309d8fJakub Hrozek i_fatal("mysql: Invalid client flags: %s", value);
a23014d69b56cbdf48ad05229c334648b5309d8fJakub Hrozek } else if (strcmp(name, "connect_timeout") == 0) {
a23014d69b56cbdf48ad05229c334648b5309d8fJakub Hrozek if (str_to_uint(value, &db->connect_timeout) < 0)
a23014d69b56cbdf48ad05229c334648b5309d8fJakub Hrozek i_fatal("mysql: Invalid read_timeout: %s", value);
a23014d69b56cbdf48ad05229c334648b5309d8fJakub Hrozek } else if (strcmp(name, "read_timeout") == 0) {
a23014d69b56cbdf48ad05229c334648b5309d8fJakub Hrozek i_fatal("mysql: Invalid read_timeout: %s", value);
a23014d69b56cbdf48ad05229c334648b5309d8fJakub Hrozek } else if (strcmp(name, "write_timeout") == 0) {
a23014d69b56cbdf48ad05229c334648b5309d8fJakub Hrozek if (str_to_uint(value, &db->write_timeout) < 0)
a23014d69b56cbdf48ad05229c334648b5309d8fJakub Hrozek i_fatal("mysql: Invalid read_timeout: %s", value);
a23014d69b56cbdf48ad05229c334648b5309d8fJakub Hrozek else if (strcmp(name, "ssl_verify_server_cert") == 0) {
a23014d69b56cbdf48ad05229c334648b5309d8fJakub Hrozek i_fatal("mysql: Unknown connect string: %s", name);
a23014d69b56cbdf48ad05229c334648b5309d8fJakub Hrozek if (db->host == NULL && db->option_file == NULL)
a23014d69b56cbdf48ad05229c334648b5309d8fJakub Hrozek i_fatal("mysql: No hosts given in connect string");
a23014d69b56cbdf48ad05229c334648b5309d8fJakub Hrozekstatic struct sql_db *driver_mysql_init_v(const char *connect_string)
a23014d69b56cbdf48ad05229c334648b5309d8fJakub Hrozek pool = pool_alloconly_create("mysql driver", 1024);
a23014d69b56cbdf48ad05229c334648b5309d8fJakub Hrozek driver_mysql_parse_connect_string(db, connect_string);
a23014d69b56cbdf48ad05229c334648b5309d8fJakub Hrozekstatic void driver_mysql_deinit_v(struct sql_db *_db)
a23014d69b56cbdf48ad05229c334648b5309d8fJakub Hrozek sql_db_set_state(&db->api, SQL_DB_STATE_DISCONNECTED);
a23014d69b56cbdf48ad05229c334648b5309d8fJakub Hrozekstatic int driver_mysql_do_query(struct mysql_db *db, const char *query)
6463ed1dcdd45416468b3fa178bd856b5a9ed2c3Jakub Hrozek /* failed */
6463ed1dcdd45416468b3fa178bd856b5a9ed2c3Jakub Hrozek sql_db_set_state(&db->api, SQL_DB_STATE_DISCONNECTED);
a23014d69b56cbdf48ad05229c334648b5309d8fJakub Hrozekstatic const char *
6463ed1dcdd45416468b3fa178bd856b5a9ed2c3Jakub Hrozekdriver_mysql_escape_string(struct sql_db *_db, const char *string)
6463ed1dcdd45416468b3fa178bd856b5a9ed2c3Jakub Hrozek /* try connecting */
6463ed1dcdd45416468b3fa178bd856b5a9ed2c3Jakub Hrozek /* FIXME: we don't have a valid connection, so fallback
6463ed1dcdd45416468b3fa178bd856b5a9ed2c3Jakub Hrozek to using default escaping. the next query will most
6463ed1dcdd45416468b3fa178bd856b5a9ed2c3Jakub Hrozek likely fail anyway so it shouldn't matter that much
6463ed1dcdd45416468b3fa178bd856b5a9ed2c3Jakub Hrozek what we return here.. Anyway, this API needs
a23014d69b56cbdf48ad05229c334648b5309d8fJakub Hrozek changing so that the escaping function could already
6463ed1dcdd45416468b3fa178bd856b5a9ed2c3Jakub Hrozek fail the query reliably. */
6463ed1dcdd45416468b3fa178bd856b5a9ed2c3Jakub Hrozek len = mysql_real_escape_string(db->mysql, to, string, len);
6463ed1dcdd45416468b3fa178bd856b5a9ed2c3Jakub Hrozekstatic void driver_mysql_exec(struct sql_db *_db, const char *query)
a23014d69b56cbdf48ad05229c334648b5309d8fJakub Hrozek mysql_prefix(db), query, mysql_error(db->mysql));
6463ed1dcdd45416468b3fa178bd856b5a9ed2c3Jakub Hrozekstatic void driver_mysql_query(struct sql_db *db, const char *query,
6463ed1dcdd45416468b3fa178bd856b5a9ed2c3Jakub Hrozekstatic struct sql_result *
a23014d69b56cbdf48ad05229c334648b5309d8fJakub Hrozekdriver_mysql_query_s(struct sql_db *_db, const char *query)
6463ed1dcdd45416468b3fa178bd856b5a9ed2c3Jakub Hrozek /* query ok */
6463ed1dcdd45416468b3fa178bd856b5a9ed2c3Jakub Hrozek result->affected_rows = mysql_affected_rows(db->mysql);
6463ed1dcdd45416468b3fa178bd856b5a9ed2c3Jakub Hrozek result->result = mysql_store_result(db->mysql);
6463ed1dcdd45416468b3fa178bd856b5a9ed2c3Jakub Hrozek /* Because we've enabled CLIENT_MULTI_RESULTS, we need to read
6463ed1dcdd45416468b3fa178bd856b5a9ed2c3Jakub Hrozek (ignore) extra results - there should not be any.
6463ed1dcdd45416468b3fa178bd856b5a9ed2c3Jakub Hrozek ret is: -1 = done, >0 = error, 0 = more results. */
6463ed1dcdd45416468b3fa178bd856b5a9ed2c3Jakub Hrozek while ((ret = mysql_next_result(db->mysql)) == 0) ;
6463ed1dcdd45416468b3fa178bd856b5a9ed2c3Jakub Hrozek (result->result != NULL || mysql_errno(db->mysql) == 0)) {
6463ed1dcdd45416468b3fa178bd856b5a9ed2c3Jakub Hrozek /* failed */
b20208b80e99abb79c00d5ec526caa9465859c52Jakub Hrozekstatic void driver_mysql_result_free(struct sql_result *_result)
b20208b80e99abb79c00d5ec526caa9465859c52Jakub Hrozek struct mysql_result *result = (struct mysql_result *)_result;
b20208b80e99abb79c00d5ec526caa9465859c52Jakub Hrozek i_assert(_result != &sql_not_connected_result);
a7797068c4deb6ce2bdbcda27c45ff1bbb4a8e78Jakub Hrozekstatic int driver_mysql_result_next_row(struct sql_result *_result)
b20208b80e99abb79c00d5ec526caa9465859c52Jakub Hrozek struct mysql_result *result = (struct mysql_result *)_result;
b20208b80e99abb79c00d5ec526caa9465859c52Jakub Hrozek struct mysql_db *db = (struct mysql_db *)_result->db;
b20208b80e99abb79c00d5ec526caa9465859c52Jakub Hrozek /* no results */
b20208b80e99abb79c00d5ec526caa9465859c52Jakub Hrozekstatic void driver_mysql_result_fetch_fields(struct mysql_result *result)
6463ed1dcdd45416468b3fa178bd856b5a9ed2c3Jakub Hrozek result->fields_count = mysql_num_fields(result->result);
a23014d69b56cbdf48ad05229c334648b5309d8fJakub Hrozek result->fields = mysql_fetch_fields(result->result);
6463ed1dcdd45416468b3fa178bd856b5a9ed2c3Jakub Hrozekstatic unsigned int
6463ed1dcdd45416468b3fa178bd856b5a9ed2c3Jakub Hrozekdriver_mysql_result_get_fields_count(struct sql_result *_result)
6463ed1dcdd45416468b3fa178bd856b5a9ed2c3Jakub Hrozek struct mysql_result *result = (struct mysql_result *)_result;
a23014d69b56cbdf48ad05229c334648b5309d8fJakub Hrozekstatic const char *
6463ed1dcdd45416468b3fa178bd856b5a9ed2c3Jakub Hrozekdriver_mysql_result_get_field_name(struct sql_result *_result, unsigned int idx)
6463ed1dcdd45416468b3fa178bd856b5a9ed2c3Jakub Hrozek struct mysql_result *result = (struct mysql_result *)_result;
6463ed1dcdd45416468b3fa178bd856b5a9ed2c3Jakub Hrozekstatic int driver_mysql_result_find_field(struct sql_result *_result,
6463ed1dcdd45416468b3fa178bd856b5a9ed2c3Jakub Hrozek struct mysql_result *result = (struct mysql_result *)_result;
6463ed1dcdd45416468b3fa178bd856b5a9ed2c3Jakub Hrozek unsigned int i;
6463ed1dcdd45416468b3fa178bd856b5a9ed2c3Jakub Hrozek if (strcmp(result->fields[i].name, field_name) == 0)
a23014d69b56cbdf48ad05229c334648b5309d8fJakub Hrozekstatic const char *
6463ed1dcdd45416468b3fa178bd856b5a9ed2c3Jakub Hrozekdriver_mysql_result_get_field_value(struct sql_result *_result,
6463ed1dcdd45416468b3fa178bd856b5a9ed2c3Jakub Hrozek unsigned int idx)
6463ed1dcdd45416468b3fa178bd856b5a9ed2c3Jakub Hrozek struct mysql_result *result = (struct mysql_result *)_result;
6463ed1dcdd45416468b3fa178bd856b5a9ed2c3Jakub Hrozekstatic const unsigned char *
6463ed1dcdd45416468b3fa178bd856b5a9ed2c3Jakub Hrozekdriver_mysql_result_get_field_value_binary(struct sql_result *_result,
6463ed1dcdd45416468b3fa178bd856b5a9ed2c3Jakub Hrozek struct mysql_result *result = (struct mysql_result *)_result;
6463ed1dcdd45416468b3fa178bd856b5a9ed2c3Jakub Hrozek unsigned long *lengths;
6463ed1dcdd45416468b3fa178bd856b5a9ed2c3Jakub Hrozekstatic const char *
6463ed1dcdd45416468b3fa178bd856b5a9ed2c3Jakub Hrozekdriver_mysql_result_find_field_value(struct sql_result *result,
6463ed1dcdd45416468b3fa178bd856b5a9ed2c3Jakub Hrozek idx = driver_mysql_result_find_field(result, field_name);
6463ed1dcdd45416468b3fa178bd856b5a9ed2c3Jakub Hrozek return driver_mysql_result_get_field_value(result, idx);
6463ed1dcdd45416468b3fa178bd856b5a9ed2c3Jakub Hrozekstatic const char *const *
6463ed1dcdd45416468b3fa178bd856b5a9ed2c3Jakub Hrozekdriver_mysql_result_get_values(struct sql_result *_result)
6463ed1dcdd45416468b3fa178bd856b5a9ed2c3Jakub Hrozek struct mysql_result *result = (struct mysql_result *)_result;
6463ed1dcdd45416468b3fa178bd856b5a9ed2c3Jakub Hrozekstatic const char *driver_mysql_result_get_error(struct sql_result *_result)
6463ed1dcdd45416468b3fa178bd856b5a9ed2c3Jakub Hrozek struct mysql_db *db = (struct mysql_db *)_result->db;
6463ed1dcdd45416468b3fa178bd856b5a9ed2c3Jakub Hrozek if ((err == CR_SERVER_GONE_ERROR || err == CR_SERVER_LOST) &&
6463ed1dcdd45416468b3fa178bd856b5a9ed2c3Jakub Hrozek errstr = t_strdup_printf("%s (idled for %u secs)",
6463ed1dcdd45416468b3fa178bd856b5a9ed2c3Jakub Hrozekdriver_mysql_transaction_begin(struct sql_db *db)
6463ed1dcdd45416468b3fa178bd856b5a9ed2c3Jakub Hrozek ctx = i_new(struct mysql_transaction_context, 1);
6463ed1dcdd45416468b3fa178bd856b5a9ed2c3Jakub Hrozek ctx->query_pool = pool_alloconly_create("mysql transaction", 1024);
6463ed1dcdd45416468b3fa178bd856b5a9ed2c3Jakub Hrozekdriver_mysql_transaction_commit(struct sql_transaction_context *ctx,
a23014d69b56cbdf48ad05229c334648b5309d8fJakub Hrozek sql_commit_callback_t *callback, void *context)
6463ed1dcdd45416468b3fa178bd856b5a9ed2c3Jakub Hrozek if (sql_transaction_commit_s(&ctx, &error) < 0)
6463ed1dcdd45416468b3fa178bd856b5a9ed2c3Jakub Hrozektransaction_send_query(struct mysql_transaction_context *ctx, const char *query,
6463ed1dcdd45416468b3fa178bd856b5a9ed2c3Jakub Hrozek struct mysql_result *result = (struct mysql_result *)_result;
6463ed1dcdd45416468b3fa178bd856b5a9ed2c3Jakub Hrozek i_assert(result->affected_rows != (my_ulonglong)-1);
6463ed1dcdd45416468b3fa178bd856b5a9ed2c3Jakub Hrozekstatic int driver_mysql_try_commit_s(struct mysql_transaction_context *ctx)
6463ed1dcdd45416468b3fa178bd856b5a9ed2c3Jakub Hrozek struct sql_transaction_context *_ctx = &ctx->ctx;
a23014d69b56cbdf48ad05229c334648b5309d8fJakub Hrozek /* try to use a transaction in any case,
6463ed1dcdd45416468b3fa178bd856b5a9ed2c3Jakub Hrozek even if it's not actually functional. */
6463ed1dcdd45416468b3fa178bd856b5a9ed2c3Jakub Hrozek if (transaction_send_query(ctx, "BEGIN", NULL) < 0) {
6463ed1dcdd45416468b3fa178bd856b5a9ed2c3Jakub Hrozek if (_ctx->db->state != SQL_DB_STATE_DISCONNECTED)
a23014d69b56cbdf48ad05229c334648b5309d8fJakub Hrozek /* we got disconnected, retry */
6463ed1dcdd45416468b3fa178bd856b5a9ed2c3Jakub Hrozek if (transaction_send_query(ctx, _ctx->head->query,
6463ed1dcdd45416468b3fa178bd856b5a9ed2c3Jakub Hrozek if (transaction_send_query(ctx, "COMMIT", NULL) < 0)
a7797068c4deb6ce2bdbcda27c45ff1bbb4a8e78Jakub Hrozekdriver_mysql_transaction_commit_s(struct sql_transaction_context *_ctx,
6463ed1dcdd45416468b3fa178bd856b5a9ed2c3Jakub Hrozek const char **error_r)
a7797068c4deb6ce2bdbcda27c45ff1bbb4a8e78Jakub Hrozek struct mysql_db *db = (struct mysql_db *)_ctx->db;
a23014d69b56cbdf48ad05229c334648b5309d8fJakub Hrozekdriver_mysql_transaction_rollback(struct sql_transaction_context *_ctx)
6463ed1dcdd45416468b3fa178bd856b5a9ed2c3Jakub Hrozekdriver_mysql_update(struct sql_transaction_context *_ctx, const char *query,
6463ed1dcdd45416468b3fa178bd856b5a9ed2c3Jakub Hrozek sql_transaction_add_query(&ctx->ctx, ctx->query_pool,
6463ed1dcdd45416468b3fa178bd856b5a9ed2c3Jakub Hrozekstatic const char *
6463ed1dcdd45416468b3fa178bd856b5a9ed2c3Jakub Hrozekdriver_mysql_escape_blob(struct sql_db *_db ATTR_UNUSED,
a23014d69b56cbdf48ad05229c334648b5309d8fJakub Hrozek .flags = SQL_DB_FLAG_BLOCKING | SQL_DB_FLAG_POOLED,
6463ed1dcdd45416468b3fa178bd856b5a9ed2c3Jakub Hrozekdriver_mysql_result_error_next_row(struct sql_result *result ATTR_UNUSED)
6463ed1dcdd45416468b3fa178bd856b5a9ed2c3Jakub Hrozekconst struct sql_result driver_mysql_error_result = {