summaryrefslogtreecommitdiffstats
path: root/src/global/dict_mysql.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/global/dict_mysql.c87
1 files changed, 54 insertions, 33 deletions
diff --git a/src/global/dict_mysql.c b/src/global/dict_mysql.c
index 3c8fe4f..133cc0d 100644
--- a/src/global/dict_mysql.c
+++ b/src/global/dict_mysql.c
@@ -83,6 +83,10 @@
#include <limits.h>
#include <errno.h>
+#if !defined(MYSQL_VERSION_ID) || MYSQL_VERSION_ID < 40000
+#error "MySQL versions <4 are no longer supported"
+#endif
+
#ifdef STRCASECMP_IN_STRINGS_H
#include <strings.h>
#endif
@@ -147,9 +151,11 @@ typedef struct {
char *username;
char *password;
char *dbname;
+ char *charset;
+ int retry_interval;
+ int idle_interval;
ARGV *hosts;
PLMYSQL *pldb;
-#if defined(MYSQL_VERSION_ID) && MYSQL_VERSION_ID >= 40000
HOST *active_host;
char *tls_cert_file;
char *tls_key_file;
@@ -159,7 +165,6 @@ typedef struct {
#if defined(DICT_MYSQL_SSL_VERIFY_SERVER_CERT)
int tls_verify_cert;
#endif
-#endif
int require_result_set;
} DICT_MYSQL;
@@ -171,15 +176,15 @@ typedef struct {
#define TYPEINET (1<<1)
#define RETRY_CONN_MAX 100
-#define RETRY_CONN_INTV 60 /* 1 minute */
-#define IDLE_CONN_INTV 60 /* 1 minute */
+#define DEF_RETRY_INTV 60 /* 1 minute */
+#define DEF_IDLE_INTV 60 /* 1 minute */
/* internal function declarations */
static PLMYSQL *plmysql_init(ARGV *);
static int plmysql_query(DICT_MYSQL *, const char *, VSTRING *, MYSQL_RES **);
static void plmysql_dealloc(PLMYSQL *);
static void plmysql_close_host(HOST *);
-static void plmysql_down_host(HOST *);
+static void plmysql_down_host(HOST *, int);
static void plmysql_connect_single(DICT_MYSQL *, HOST *);
static const char *dict_mysql_lookup(DICT *, const char *);
DICT *dict_mysql_open(const char *, int, int);
@@ -205,13 +210,21 @@ static void dict_mysql_quote(DICT *dict, const char *name, VSTRING *result)
buflen = 2 * len + 1;
VSTRING_SPACE(result, buflen);
-#if defined(MYSQL_VERSION_ID) && MYSQL_VERSION_ID >= 40000
- if (dict_mysql->active_host)
- mysql_real_escape_string(dict_mysql->active_host->db,
- vstring_end(result), name, len);
- else
+ if (dict_mysql->active_host == 0)
+ msg_panic("dict_mysql_quote: no active host");
+#if MYSQL_VERSION_ID >= 50706 && !defined(MARIADB_VERSION_ID)
+ mysql_real_escape_string_quote(dict_mysql->active_host->db,
+ vstring_end(result), name, len, '\'');
+#else
+ if (mysql_real_escape_string(dict_mysql->active_host->db,
+ vstring_end(result), name, len) ==
+ (unsigned long) -1) {
+ msg_warn("dict_mysql: host (%s) cannot escape input string: >%s<",
+ dict_mysql->active_host->hostname,
+ mysql_error(dict_mysql->active_host->db));
+ dict_mysql->active_host->stat = STATFAIL;
+ }
#endif
- mysql_escape_string(vstring_end(result), name, len);
VSTRING_SKIP(result);
}
@@ -231,7 +244,6 @@ static const char *dict_mysql_lookup(DICT *dict, const char *name)
int numrows;
int expansion;
const char *r;
- db_quote_callback_t quote_func = dict_mysql_quote;
int domain_rc;
dict->error = 0;
@@ -241,7 +253,7 @@ static const char *dict_mysql_lookup(DICT *dict, const char *name)
*/
#ifdef SNAPSHOT
if ((dict->flags & DICT_FLAG_UTF8_ACTIVE) == 0
- && !valid_utf8_string(name, strlen(name))) {
+ && !valid_utf8_stringz(name)) {
if (msg_verbose)
msg_info("%s: %s: Skipping lookup of non-UTF-8 key '%s'",
myname, dict_mysql->parser->name, name);
@@ -291,11 +303,8 @@ static const char *dict_mysql_lookup(DICT *dict, const char *name)
* quoting happens separately for each connection, we don't bother with
* quoting...
*/
-#if defined(MYSQL_VERSION_ID) && MYSQL_VERSION_ID >= 40000
- quote_func = 0;
-#endif
if (!db_common_expand(dict_mysql->ctx, dict_mysql->query,
- name, 0, query, quote_func))
+ name, 0, query, (db_quote_callback_t) 0))
return (0);
/* do the query - set dict->error & cleanup if there's an error */
@@ -439,8 +448,12 @@ static int plmysql_query(DICT_MYSQL *dict_mysql,
{
HOST *host;
MYSQL_RES *first_result = 0;
+
+ /* In case all hosts are down. */
int query_error = 1;
+ errno = ENOTSUP;
+
/*
* Helper to avoid spamming the log with warnings.
*/
@@ -454,8 +467,6 @@ static int plmysql_query(DICT_MYSQL *dict_mysql,
while ((host = dict_mysql_get_active(dict_mysql)) != NULL) {
-#if defined(MYSQL_VERSION_ID) && MYSQL_VERSION_ID >= 40000
-
/*
* The active host is used to escape strings in the context of the
* active connection's character encoding.
@@ -465,8 +476,14 @@ static int plmysql_query(DICT_MYSQL *dict_mysql,
VSTRING_TERMINATE(query);
db_common_expand(dict_mysql->ctx, dict_mysql->query,
name, 0, query, dict_mysql_quote);
+ /* Check for potential dict_mysql_quote() failure. */
+ if (host->stat == STATFAIL) {
+ plmysql_down_host(host, dict_mysql->retry_interval);
+ continue;
+ }
+ if (msg_verbose)
+ msg_info("expanded and quoted query: >%s<", vstring_str(query));
dict_mysql->active_host = 0;
-#endif
query_error = 0;
errno = 0;
@@ -546,7 +563,7 @@ static int plmysql_query(DICT_MYSQL *dict_mysql,
* See what we got.
*/
if (query_error) {
- plmysql_down_host(host);
+ plmysql_down_host(host, dict_mysql->retry_interval);
if (errno == 0)
errno = ENOTSUP;
if (first_result) {
@@ -559,7 +576,7 @@ static int plmysql_query(DICT_MYSQL *dict_mysql,
dict_mysql->dict.type, dict_mysql->dict.name,
host->hostname);
event_request_timer(dict_mysql_event, (void *) host,
- IDLE_CONN_INTV);
+ dict_mysql->idle_interval);
break;
}
}
@@ -581,7 +598,6 @@ static void plmysql_connect_single(DICT_MYSQL *dict_mysql, HOST *host)
mysql_options(host->db, MYSQL_READ_DEFAULT_FILE, dict_mysql->option_file);
if (dict_mysql->option_group && dict_mysql->option_group[0])
mysql_options(host->db, MYSQL_READ_DEFAULT_GROUP, dict_mysql->option_group);
-#if defined(MYSQL_VERSION_ID) && MYSQL_VERSION_ID >= 40000
if (dict_mysql->tls_key_file || dict_mysql->tls_cert_file ||
dict_mysql->tls_CAfile || dict_mysql->tls_CApath || dict_mysql->tls_ciphers)
mysql_ssl_set(host->db,
@@ -593,7 +609,6 @@ static void plmysql_connect_single(DICT_MYSQL *dict_mysql, HOST *host)
mysql_options(host->db, DICT_MYSQL_SSL_VERIFY_SERVER_CERT,
&dict_mysql->tls_verify_cert);
#endif
-#endif
if (mysql_real_connect(host->db,
(host->type == TYPEINET ? host->name : 0),
dict_mysql->username,
@@ -602,6 +617,12 @@ static void plmysql_connect_single(DICT_MYSQL *dict_mysql, HOST *host)
host->port,
(host->type == TYPEUNIX ? host->name : 0),
CLIENT_MULTI_RESULTS)) {
+ if (mysql_set_character_set(host->db, dict_mysql->charset) != 0) {
+ msg_warn("dict_mysql: mysql_set_character_set '%s' failed: %s",
+ dict_mysql->charset, mysql_error(host->db));
+ plmysql_down_host(host, dict_mysql->retry_interval);
+ return;
+ }
if (msg_verbose)
msg_info("dict_mysql: successful connection to host %s",
host->hostname);
@@ -609,7 +630,7 @@ static void plmysql_connect_single(DICT_MYSQL *dict_mysql, HOST *host)
} else {
msg_warn("connect to mysql server %s: %s",
host->hostname, mysql_error(host->db));
- plmysql_down_host(host);
+ plmysql_down_host(host, dict_mysql->retry_interval);
}
}
@@ -625,11 +646,11 @@ static void plmysql_close_host(HOST *host)
* plmysql_down_host - close a failed connection AND set a "stay away from
* this host" timer
*/
-static void plmysql_down_host(HOST *host)
+static void plmysql_down_host(HOST *host, int retry_interval)
{
mysql_close(host->db);
host->db = 0;
- host->ts = time((time_t *) 0) + RETRY_CONN_INTV;
+ host->ts = time((time_t *) 0) + retry_interval;
host->stat = STATFAIL;
event_cancel_timer(dict_mysql_event, (void *) host);
}
@@ -646,10 +667,14 @@ static void mysql_parse_config(DICT_MYSQL *dict_mysql, const char *mysqlcf)
dict_mysql->username = cfg_get_str(p, "user", "", 0, 0);
dict_mysql->password = cfg_get_str(p, "password", "", 0, 0);
dict_mysql->dbname = cfg_get_str(p, "dbname", "", 1, 0);
+ dict_mysql->charset = cfg_get_str(p, "charset", "utf8mb4", 1, 0);
+ dict_mysql->retry_interval = cfg_get_int(p, "retry_interval",
+ DEF_RETRY_INTV, 1, 0);
+ dict_mysql->idle_interval = cfg_get_int(p, "idle_interval",
+ DEF_IDLE_INTV, 1, 0);
dict_mysql->result_format = cfg_get_str(p, "result_format", "%s", 1, 0);
dict_mysql->option_file = cfg_get_str(p, "option_file", NULL, 0, 0);
dict_mysql->option_group = cfg_get_str(p, "option_group", "client", 0, 0);
-#if defined(MYSQL_VERSION_ID) && MYSQL_VERSION_ID >= 40000
dict_mysql->tls_key_file = cfg_get_str(p, "tls_key_file", NULL, 0, 0);
dict_mysql->tls_cert_file = cfg_get_str(p, "tls_cert_file", NULL, 0, 0);
dict_mysql->tls_CAfile = cfg_get_str(p, "tls_CAfile", NULL, 0, 0);
@@ -658,7 +683,6 @@ static void mysql_parse_config(DICT_MYSQL *dict_mysql, const char *mysqlcf)
#if defined(DICT_MYSQL_SSL_VERIFY_SERVER_CERT)
dict_mysql->tls_verify_cert = cfg_get_bool(p, "tls_verify_cert", -1);
#endif
-#endif
dict_mysql->require_result_set = cfg_get_bool(p, "require_result_set", 1);
/*
@@ -741,9 +765,7 @@ DICT *dict_mysql_open(const char *name, int open_flags, int dict_flags)
dict_mysql->dict.flags = dict_flags;
dict_mysql->parser = parser;
mysql_parse_config(dict_mysql, name);
-#if defined(MYSQL_VERSION_ID) && MYSQL_VERSION_ID >= 40000
dict_mysql->active_host = 0;
-#endif
dict_mysql->pldb = plmysql_init(dict_mysql->hosts);
if (dict_mysql->pldb == NULL)
msg_fatal("couldn't initialize pldb!\n");
@@ -826,13 +848,13 @@ static void dict_mysql_close(DICT *dict)
myfree(dict_mysql->username);
myfree(dict_mysql->password);
myfree(dict_mysql->dbname);
+ myfree(dict_mysql->charset);
myfree(dict_mysql->query);
myfree(dict_mysql->result_format);
if (dict_mysql->option_file)
myfree(dict_mysql->option_file);
if (dict_mysql->option_group)
myfree(dict_mysql->option_group);
-#if defined(MYSQL_VERSION_ID) && MYSQL_VERSION_ID >= 40000
if (dict_mysql->tls_key_file)
myfree(dict_mysql->tls_key_file);
if (dict_mysql->tls_cert_file)
@@ -843,7 +865,6 @@ static void dict_mysql_close(DICT *dict)
myfree(dict_mysql->tls_CApath);
if (dict_mysql->tls_ciphers)
myfree(dict_mysql->tls_ciphers);
-#endif
if (dict_mysql->hosts)
argv_free(dict_mysql->hosts);
if (dict_mysql->ctx)