driver-mysql.c revision c95fc202215d2451372599db7092b16459f360a3
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen/* Copyright (C) 2003-2004 Timo Sirainen, Alex Howansky */
06b0c3be9905099038964b068216bbed155701deTimo Sirainen/* Abort connect() if it can't connect within this time. */
6bca3405636e3ec95724350c3a10d6fcb737782aTimo Sirainen/* Minimum delay between reconnecting to same server */
6bca3405636e3ec95724350c3a10d6fcb737782aTimo Sirainen/* Maximum time to avoiding reconnecting to same server */
6bca3405636e3ec95724350c3a10d6fcb737782aTimo Sirainen/* If no servers are connected but a query is requested, try reconnecting to
6bca3405636e3ec95724350c3a10d6fcb737782aTimo Sirainen next server which has been disconnected longer than this (with a single
6bca3405636e3ec95724350c3a10d6fcb737782aTimo Sirainen server setup this is really the "max delay" and the CONNECT_MAX_DELAY
6bca3405636e3ec95724350c3a10d6fcb737782aTimo Sirainen is never used). */
6bca3405636e3ec95724350c3a10d6fcb737782aTimo Sirainen const char *user, *password, *dbname, *unix_socket;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen const char *ssl_cert, *ssl_key, *ssl_ca, *ssl_ca_path, *ssl_cipher;
5ab2ee0b9b7ad3867fcfd2a31fda0790370fbbbdTimo Sirainen array_t ARRAY_DEFINE(connections, struct mysql_connection);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainenextern struct sql_result driver_mysql_error_result;
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainenstatic bool driver_mysql_connect(struct mysql_connection *conn)
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen /* don't try reconnecting more than once a second */
6bca3405636e3ec95724350c3a10d6fcb737782aTimo Sirainen if (conn->last_connect + (time_t)conn->connect_delay > now)
6bca3405636e3ec95724350c3a10d6fcb737782aTimo Sirainen if (!conn->ssl_set && (db->ssl_ca != NULL || db->ssl_ca_path != NULL)) {
6bca3405636e3ec95724350c3a10d6fcb737782aTimo Sirainen mysql_ssl_set(conn->mysql, db->ssl_key, db->ssl_cert,
6bca3405636e3ec95724350c3a10d6fcb737782aTimo Sirainen "(remove ssl_ca and ssl_ca_path settings)");
06b0c3be9905099038964b068216bbed155701deTimo Sirainen failed = mysql_real_connect(conn->mysql, host, db->user, db->password,
6bca3405636e3ec95724350c3a10d6fcb737782aTimo Sirainen /* increase delay between reconnections to this
6bca3405636e3ec95724350c3a10d6fcb737782aTimo Sirainen i_error("mysql: Connect failed to %s (%s): %s - "
6bca3405636e3ec95724350c3a10d6fcb737782aTimo Sirainen "waiting for %u seconds before retry",
200bedfb0a0472b74b2ec50c0a36bb167f39ea76Timo Sirainen host != NULL ? host : unix_socket, db->dbname,
200bedfb0a0472b74b2ec50c0a36bb167f39ea76Timo Sirainen mysql_error(conn->mysql), conn->connect_delay);
6bca3405636e3ec95724350c3a10d6fcb737782aTimo Sirainen conn->ssl_set ? " using SSL" : "", db->dbname);
a0b89f3b1df99b3a32f44623f13ad1893118825bTimo Sirainenstatic int driver_mysql_connect_all(struct sql_db *_db)
5ab2ee0b9b7ad3867fcfd2a31fda0790370fbbbdTimo Sirainen unsigned int i, count;
5ab2ee0b9b7ad3867fcfd2a31fda0790370fbbbdTimo Sirainen conn = array_get_modifyable(&db->connections, &count);
5ab2ee0b9b7ad3867fcfd2a31fda0790370fbbbdTimo Sirainen for (i = 0; i < count; i++) {
6bca3405636e3ec95724350c3a10d6fcb737782aTimo Sirainenstatic void driver_mysql_connection_add(struct mysql_db *db, const char *host)
6bca3405636e3ec95724350c3a10d6fcb737782aTimo Sirainenstatic void driver_mysql_connection_free(struct mysql_connection *conn)
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainenstatic void driver_mysql_parse_connect_string(struct mysql_db *db,
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen const char **field;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen args = t_strsplit_spaces(connect_string, " ");
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen i_fatal("mysql: Missing value in connect string: %s",
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen i_fatal("mysql: Unknown connect string: %s", name);
fedb73c7e918653877286ede0fe18029b3cce7d3Timo Sirainen /* Updates require this because everything is committed in one large
fedb73c7e918653877286ede0fe18029b3cce7d3Timo Sirainen SQL statement. */
6bca3405636e3ec95724350c3a10d6fcb737782aTimo Sirainen i_fatal("mysql: No hosts given in connect string");
0371406d952fe51367c7be91703e5634b7d9d225Timo Sirainenstatic struct sql_db *_driver_mysql_init(const char *connect_string)
367c05967091a2cbfce59b7f274f55b1a0f9e8c9Timo Sirainen pool = pool_alloconly_create("mysql driver", 512);
5ab2ee0b9b7ad3867fcfd2a31fda0790370fbbbdTimo Sirainen ARRAY_CREATE(&db->connections, pool, struct mysql_connection, 6);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen driver_mysql_parse_connect_string(db, connect_string);
0371406d952fe51367c7be91703e5634b7d9d225Timo Sirainenstatic void _driver_mysql_deinit(struct sql_db *_db)
5ab2ee0b9b7ad3867fcfd2a31fda0790370fbbbdTimo Sirainen unsigned int i, count;
5ab2ee0b9b7ad3867fcfd2a31fda0790370fbbbdTimo Sirainen conn = array_get_modifyable(&db->connections, &count);
5ab2ee0b9b7ad3867fcfd2a31fda0790370fbbbdTimo Sirainen for (i = 0; i < count; i++)
ccffb125d94adff0ad776de5a96e22f864d6fb0aTimo Sirainendriver_mysql_get_flags(struct sql_db *db __attr_unused__)
6bca3405636e3ec95724350c3a10d6fcb737782aTimo Sirainenstatic int driver_mysql_connection_do_query(struct mysql_connection *conn,
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen for (i = 0; i < 2; i++) {
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen /* connection lost - try immediate reconnect */
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen /* connected -> lost it -> connected -> lost again */
6bca3405636e3ec95724350c3a10d6fcb737782aTimo Sirainenstatic int driver_mysql_do_query(struct mysql_db *db, const char *query,
5ab2ee0b9b7ad3867fcfd2a31fda0790370fbbbdTimo Sirainen conn = array_get_modifyable(&db->connections, &count);
6bca3405636e3ec95724350c3a10d6fcb737782aTimo Sirainen /* go through the connections in round robin. if the connection
6bca3405636e3ec95724350c3a10d6fcb737782aTimo Sirainen isn't available, try next one that is. */
6bca3405636e3ec95724350c3a10d6fcb737782aTimo Sirainen ret = driver_mysql_connection_do_query(&conn[i], query);
6bca3405636e3ec95724350c3a10d6fcb737782aTimo Sirainen /* success / failure */
6bca3405636e3ec95724350c3a10d6fcb737782aTimo Sirainen /* not connected, try next one */
6bca3405636e3ec95724350c3a10d6fcb737782aTimo Sirainen } while (i != start);
6bca3405636e3ec95724350c3a10d6fcb737782aTimo Sirainen /* none are connected. connect_delays may have gotten too high,
6bca3405636e3ec95724350c3a10d6fcb737782aTimo Sirainen reset all of them to see if some are still alive. */
5ab2ee0b9b7ad3867fcfd2a31fda0790370fbbbdTimo Sirainen for (i = 0; i < count; i++)
24ce0c343cefe54af841871fa39dbc3464028b06Timo Sirainendriver_mysql_escape_string(struct sql_db *_db, const char *string)
c95fc202215d2451372599db7092b16459f360a3Timo Sirainen unsigned int i, count;
c95fc202215d2451372599db7092b16459f360a3Timo Sirainen /* All the connections should be identical, so just use the first
c95fc202215d2451372599db7092b16459f360a3Timo Sirainen connected one */
c95fc202215d2451372599db7092b16459f360a3Timo Sirainen conn = array_get_modifyable(&db->connections, &count);
c95fc202215d2451372599db7092b16459f360a3Timo Sirainen for (i = 0; i < count; i++) {
c95fc202215d2451372599db7092b16459f360a3Timo Sirainen /* so, try connecting.. */
c95fc202215d2451372599db7092b16459f360a3Timo Sirainen for (i = 0; i < count; i++) {
c95fc202215d2451372599db7092b16459f360a3Timo Sirainen /* FIXME: we don't have a valid connection, so fallback
c95fc202215d2451372599db7092b16459f360a3Timo Sirainen to using default escaping. the next query will most
c95fc202215d2451372599db7092b16459f360a3Timo Sirainen likely fail anyway so it shouldn't matter that much
c95fc202215d2451372599db7092b16459f360a3Timo Sirainen what we return here.. Anyway, this API needs
c95fc202215d2451372599db7092b16459f360a3Timo Sirainen changing so that the escaping function could already
c95fc202215d2451372599db7092b16459f360a3Timo Sirainen fail the query reliably. */
c95fc202215d2451372599db7092b16459f360a3Timo Sirainen len = mysql_real_escape_string(conn[i].mysql, to, string, len);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainenstatic void driver_mysql_exec(struct sql_db *_db, const char *query)
6bca3405636e3ec95724350c3a10d6fcb737782aTimo Sirainen (void)driver_mysql_do_query(db, query, &conn);
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainenstatic void driver_mysql_query(struct sql_db *db, const char *query,
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen sql_query_callback_t *callback, void *context)
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainenstatic struct sql_result *
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainendriver_mysql_query_s(struct sql_db *_db, const char *query)
6bca3405636e3ec95724350c3a10d6fcb737782aTimo Sirainen switch (driver_mysql_do_query(db, query, &conn)) {
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen /* not connected */
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen /* query ok */
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen result->result = mysql_store_result(conn->mysql);
fedb73c7e918653877286ede0fe18029b3cce7d3Timo Sirainen if (result->result != NULL || mysql_errno(conn->mysql) == 0)
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen /* fallback */
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainenstatic void driver_mysql_result_free(struct sql_result *_result)
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen struct mysql_result *result = (struct mysql_result *)_result;
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen if (_result == &sql_not_connected_result || _result->callback)
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainenstatic int driver_mysql_result_next_row(struct sql_result *_result)
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen struct mysql_result *result = (struct mysql_result *)_result;
fedb73c7e918653877286ede0fe18029b3cce7d3Timo Sirainen /* no results */
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen result->row = mysql_fetch_row(result->result);
fedb73c7e918653877286ede0fe18029b3cce7d3Timo Sirainen return mysql_errno(result->conn->mysql) != 0 ? -1 : 0;
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;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen if (strcmp(result->fields[i].name, field_name) == 0)
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainenstatic const char *
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainendriver_mysql_result_get_field_value(struct sql_result *_result,
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen unsigned int idx)
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen struct mysql_result *result = (struct mysql_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);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainenstatic const char *const *
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainendriver_mysql_result_get_values(struct sql_result *_result)
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen struct mysql_result *result = (struct mysql_result *)_result;
6bca3405636e3ec95724350c3a10d6fcb737782aTimo Sirainenstatic const char *driver_mysql_result_get_error(struct sql_result *_result)
6bca3405636e3ec95724350c3a10d6fcb737782aTimo Sirainen struct mysql_result *result = (struct mysql_result *)_result;
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainendriver_mysql_transaction_begin(struct sql_db *db)
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen ctx = i_new(struct mysql_transaction_context, 1);
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainendriver_mysql_transaction_commit(struct sql_transaction_context *ctx,
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen sql_commit_callback_t *callback, void *context)
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen if (sql_transaction_commit_s(&ctx, &error) < 0)
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainendriver_mysql_transaction_commit_s(struct sql_transaction_context *_ctx,
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen const char **error_r)
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen result = sql_query_s(_ctx->db, str_c(ctx->queries));
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainendriver_mysql_transaction_rollback(struct sql_transaction_context *_ctx)
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainendriver_mysql_update(struct sql_transaction_context *_ctx, const char *query)
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen /* FIXME: with mysql we're just appending everything into one big
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen string which gets committed in sql_transaction_commit(). we could
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen avoid this if we knew for sure that transactions actually worked,
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen but I don't know how to do that.. */
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen /* try to use a transaction in any case,
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen even if it doesn't work. */
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainendriver_mysql_result_error_next_row(struct sql_result *result __attr_unused__)