]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
dict-sql: Use prepared statements
authorTimo Sirainen <timo.sirainen@dovecot.fi>
Thu, 7 Sep 2017 12:40:16 +0000 (15:40 +0300)
committerAki Tuomi <aki.tuomi@dovecot.fi>
Fri, 8 Sep 2017 10:18:32 +0000 (13:18 +0300)
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.

src/lib-dict/dict-sql-private.h
src/lib-dict/dict-sql.c

index 8c6ea392dc68d8aaaa788961107f3ccc9f1359f4..8b1d148420cb673ed39b10932cc27ce43d406a58 100644 (file)
@@ -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;
 };
 
index c14425580fa48a10e0a2486410d777556bcea2cb..9e57732f5537392fbd4909552d51b20be78b2540 100644 (file)
@@ -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), &params);
+       *stmt_r = sql_dict_statement_init(dict, str_c(query), &params);
        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), &params);
+       *stmt_r = sql_dict_statement_init(dict, str_c(query), &params);
        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);