diff options
Diffstat (limited to '')
-rw-r--r-- | src/global/dict_mysql.c | 87 |
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) |