From: Timo Sirainen Date: Thu, 7 Sep 2017 12:40:16 +0000 (+0300) Subject: dict-sql: Use prepared statements X-Git-Tag: 2.3.0.rc1~1058 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=6a3f466e0ce75c3356932c5a28894285153e3156;p=thirdparty%2Fdovecot%2Fcore.git dict-sql: Use prepared statements Create a hash table of query template -> prepared statement and fill it out as needed. This could have been done some alternative ways that wouldn't require building the string first, but this should still be fast enough and much easier to implement. --- diff --git a/src/lib-dict/dict-sql-private.h b/src/lib-dict/dict-sql-private.h index 8c6ea392dc..8b1d148420 100644 --- a/src/lib-dict/dict-sql-private.h +++ b/src/lib-dict/dict-sql-private.h @@ -9,6 +9,9 @@ struct sql_dict { const char *username; const struct dict_sql_settings *set; + /* query template => prepared statement */ + HASH_TABLE(const char *, struct sql_prepared_statement *) prep_stmt_hash; + bool has_on_duplicate_key:1; }; diff --git a/src/lib-dict/dict-sql.c b/src/lib-dict/dict-sql.c index c14425580f..9e57732f55 100644 --- a/src/lib-dict/dict-sql.c +++ b/src/lib-dict/dict-sql.c @@ -4,6 +4,7 @@ #include "array.h" #include "istream.h" #include "hex-binary.h" +#include "hash.h" #include "str.h" #include "sql-api-private.h" #include "sql-db-cache.h" @@ -106,14 +107,36 @@ sql_dict_init(struct dict *driver, const char *uri, dict->db = sql_db_cache_new(dict_sql_db_cache, driver->name, dict->set->connect); + if ((sql_get_flags(dict->db) & SQL_DB_FLAG_PREP_STATEMENTS) != 0) { + hash_table_create(&dict->prep_stmt_hash, dict->pool, + 0, str_hash, strcmp); + } *dict_r = &dict->dict; return 0; } +static void sql_dict_prep_stmt_hash_free(struct sql_dict *dict) +{ + struct hash_iterate_context *iter; + struct sql_prepared_statement *prep_stmt; + const char *query; + + if (!hash_table_is_created(dict->prep_stmt_hash)) + return; + + iter = hash_table_iterate_init(dict->prep_stmt_hash); + while (hash_table_iterate(iter, dict->prep_stmt_hash, &query, &prep_stmt)) + sql_prepared_statement_deinit(&prep_stmt); + hash_table_iterate_deinit(&iter); + + hash_table_destroy(&dict->prep_stmt_hash); +} + static void sql_dict_deinit(struct dict *_dict) { struct sql_dict *dict = (struct sql_dict *)_dict; + sql_dict_prep_stmt_hash_free(dict); sql_deinit(&dict->db); pool_unref(&dict->pool); } @@ -239,12 +262,27 @@ sql_dict_statement_bind(struct sql_statement *stmt, unsigned int column_idx, } static struct sql_statement * -sql_dict_statement_init(struct sql_db *db, const char *query, +sql_dict_statement_init(struct sql_dict *dict, const char *query, const ARRAY_TYPE(sql_dict_param) *params) { - struct sql_statement *stmt = sql_statement_init(db, query); + struct sql_statement *stmt; + struct sql_prepared_statement *prep_stmt; const struct sql_dict_param *param; + if (hash_table_is_created(dict->prep_stmt_hash)) { + prep_stmt = hash_table_lookup(dict->prep_stmt_hash, query); + if (prep_stmt == NULL) { + const char *query_dup = p_strdup(dict->pool, query); + prep_stmt = sql_prepared_statement_init(dict->db, query); + hash_table_insert(dict->prep_stmt_hash, query_dup, prep_stmt); + } + stmt = sql_statement_init_prepared(prep_stmt); + } else { + /* Prepared statements not supported by the backend. + Just use regular statements to avoid wasting memory. */ + stmt = sql_statement_init(dict->db, query); + } + array_foreach(params, param) { sql_dict_statement_bind(stmt, array_foreach_idx(params, param), param); @@ -424,7 +462,7 @@ sql_lookup_get_query(struct sql_dict *dict, const char *key, "sql dict lookup: Failed to lookup key %s: %s", key, error); return -1; } - *stmt_r = sql_dict_statement_init(dict->db, str_c(query), ¶ms); + *stmt_r = sql_dict_statement_init(dict, str_c(query), ¶ms); return 0; } @@ -682,7 +720,7 @@ sql_dict_iterate_build_next_query(struct sql_dict_iterate_context *ctx, (unsigned long long)(ctx->ctx.max_rows - ctx->ctx.row_count)); } - *stmt_r = sql_dict_statement_init(dict->db, str_c(query), ¶ms); + *stmt_r = sql_dict_statement_init(dict, str_c(query), ¶ms); ctx->map = map; return 1; } @@ -984,7 +1022,7 @@ sql_dict_transaction_stmt_init(struct sql_dict_transaction_context *ctx, { struct sql_dict *dict = (struct sql_dict *)ctx->ctx.dict; struct sql_statement *stmt = - sql_dict_statement_init(dict->db, query, params); + sql_dict_statement_init(dict, query, params); if (ctx->ctx.timestamp.tv_sec != 0) sql_statement_set_timestamp(stmt, &ctx->ctx.timestamp);