/
niceSOFT
/
dovecot
Обзор
Документация
Войти
/
niceSOFT
/
dovecot
Код
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/lib-sql/sql-api.c
1 014 строк
24 KB
Michael M Slusarz
COPYING: Simplify and standardize copyright handling
03 июл 2026, 02:25
03 июл 2026, 02:25
2a5809c
Код
Авторство
О чём код?
/* Copyright (c) Dovecot authors, see top-level COPYING file */ #include "lib.h" #include "array.h" #include "ioloop.h" #include "hash.h" #include "llist.h" #include "str.h" #include "time-util.h" #include "settings.h" #include "settings-parser.h" #include "sql-api-private.h" #include <time.h> struct sql_query_result_delayed { struct sql_query_result_delayed *prev, *next; struct sql_db *db; struct timeout *to; struct sql_result *result; sql_query_callback_t *callback; void *context; }; struct sql_commit_result_delayed { struct sql_commit_result_delayed *prev, *next; struct sql_db *db; struct timeout *to; char *error; sql_commit_callback_t *callback; void *context; }; struct event_category event_category_sql = { .name = "sql", }; #undef DEF #define DEF(type, name) \ SETTING_DEFINE_STRUCT_##type(#name, name, struct sql_settings) static const struct setting_define sql_setting_defines[] = { DEF(STR, sql_driver), SETTING_DEFINE_LIST_END }; static const struct sql_settings sql_default_settings = { .sql_driver = "", }; const struct setting_parser_info sql_setting_parser_info = { .name = "sql", .defines = sql_setting_defines, .defaults = &sql_default_settings, .struct_size = sizeof(struct sql_settings), .pool_offset1 = 1 + offsetof(struct sql_settings, pool), }; struct sql_db_module_register sql_db_module_register = { 0 }; ARRAY_TYPE(sql_drivers) sql_drivers; static void sql_query_delayed_callback(struct sql_query_result_delayed *cb); static void sql_commit_delayed_callback(struct sql_commit_result_delayed *cb); void sql_drivers_init_without_drivers(void) { i_array_init(&sql_drivers, 8); } void sql_drivers_deinit_without_drivers(void) { array_free(&sql_drivers); } extern struct sql_db driver_sqlpool_db; void sql_drivers_init(void) { /* Access driver-sqlpool in some way to avoid dropping the .o entirely when linking libsql.a to auth process when sql drivers are built as plugins. */ if (driver_sqlpool_db.v.init != NULL) i_unreached(); sql_drivers_init_without_drivers(); sql_drivers_init_all(); } void sql_drivers_deinit(void) { sql_drivers_deinit_all(); sql_drivers_deinit_without_drivers(); } static const struct sql_db *sql_driver_lookup(const char *name) { const struct sql_db *const *drivers; unsigned int i, count; drivers = array_get(&sql_drivers, &count); for (i = 0; i < count; i++) { if (strcmp(drivers[i]->name, name) == 0) return drivers[i]; } return NULL; } void sql_driver_register(const struct sql_db *driver) { if (sql_driver_lookup(driver->name) != NULL) { i_fatal("sql_driver_register(%s): Already registered", driver->name); } array_push_back(&sql_drivers, &driver); } void sql_driver_unregister(const struct sql_db *driver) { unsigned int i; if (!array_lsearch_ptr_idx(&sql_drivers, driver, &i)) i_unreached(); array_delete(&sql_drivers, i, 1); } int sql_init_auto(struct event *event, struct sql_db **db_r, const char **error_r) { const struct sql_db *driver; struct sql_db *db; struct sql_settings *sql_set; const char *error; i_assert(event != NULL); if (settings_get(event, &sql_setting_parser_info, 0, &sql_set, error_r) < 0) return -1; if (sql_set->sql_driver[0] == '\0') { *error_r = "sql_driver setting is empty"; settings_free(sql_set); return 0; } driver = sql_driver_lookup(sql_set->sql_driver); if (driver == NULL) { *error_r = t_strdup_printf("Unknown database driver '%s'", sql_set->sql_driver); settings_free(sql_set); return -1; } if (driver->v.init(event, &db, &error) < 0) { *error_r = t_strdup_printf("sql %s: %s", sql_set->sql_driver, error); settings_free(sql_set); return -1; } settings_free(sql_set); *db_r = db; return 1; } void sql_init_common(struct sql_db *db) { db->refcount = 1; i_array_init(&db->module_contexts, 5); hash_table_create(&db->prepared_stmt_hash, default_pool, 0, str_hash, strcmp); } void sql_ref(struct sql_db *db) { i_assert(db->refcount > 0); db->refcount++; } static void default_sql_prepared_statement_deinit(struct sql_prepared_statement *prep_stmt) { i_free(prep_stmt->query_template); i_free(prep_stmt); } static void sql_prepared_statements_free(struct sql_db *db) { struct hash_iterate_context *iter; struct sql_prepared_statement *prep_stmt; char *query; iter = hash_table_iterate_init(db->prepared_stmt_hash); while (hash_table_iterate(iter, db->prepared_stmt_hash, &query, &prep_stmt)) { i_assert(prep_stmt->refcount == 0); if (prep_stmt->db->v.prepared_statement_deinit != NULL) prep_stmt->db->v.prepared_statement_deinit(prep_stmt); else default_sql_prepared_statement_deinit(prep_stmt); } hash_table_iterate_deinit(&iter); hash_table_clear(db->prepared_stmt_hash, TRUE); } void sql_unref(struct sql_db **_db) { struct sql_db *db = *_db; *_db = NULL; i_assert(db->refcount > 0); if (db->v.unref != NULL) db->v.unref(db); if (--db->refcount > 0) return; timeout_remove(&db->to_reconnect); sql_prepared_statements_free(db); hash_table_destroy(&db->prepared_stmt_hash); db->v.deinit(db); } enum sql_db_flags sql_get_flags(struct sql_db *db) { if (db->v.get_flags != NULL) return db->v.get_flags(db); else return db->flags; } int sql_connect(struct sql_db *db) { time_t now; switch (db->state) { case SQL_DB_STATE_DISCONNECTED: break; case SQL_DB_STATE_CONNECTING: return 0; default: return 1; } /* don't try reconnecting more than once a second */ now = time(NULL); if (db->last_connect_try + (time_t)db->connect_delay > now) return -1; db->last_connect_try = now; return db->v.connect(db); } static void sql_call_delayed_callbacks(struct sql_db *db) { /* flush any pending results */ while (db->query_delayed_list != NULL) sql_query_delayed_callback(db->query_delayed_list); while (db->commit_delayed_list != NULL) sql_commit_delayed_callback(db->commit_delayed_list); } void sql_disconnect(struct sql_db *db) { timeout_remove(&db->to_reconnect); sql_call_delayed_callbacks(db); db->v.disconnect(db); } const char *sql_escape_string(struct sql_db *db, const char *string) { return db->v.escape_string(db, string); } const char *sql_escape_blob(struct sql_db *db, const unsigned char *data, size_t size) { return db->v.escape_blob(db, data, size); } void sql_exec(struct sql_db *db, const char *query) { db->v.exec(db, query); } static void sql_query_delayed_callback(struct sql_query_result_delayed *cb) { timeout_remove(&cb->to); DLLIST_REMOVE(&cb->db->query_delayed_list, cb); cb->callback(cb->result, cb->context); sql_result_unref(cb->result); i_free(cb); } #undef sql_query void sql_query(struct sql_db *db, const char *query, sql_query_callback_t *callback, void *context) { if (db->v.query != NULL) { db->v.query(db, query, callback, context); return; } struct sql_query_result_delayed *cb = i_new(struct sql_query_result_delayed, 1); cb->db = db; cb->result = sql_query_s(db, query); cb->callback = callback; cb->context = context; cb->to = timeout_add_short(0, sql_query_delayed_callback, cb); DLLIST_PREPEND(&db->query_delayed_list, cb); } struct sql_result *sql_query_s(struct sql_db *db, const char *query) { return db->v.query_s(db, query); } static struct sql_prepared_statement * default_sql_prepared_statement_init(struct sql_db *db, const char *query_template) { struct sql_prepared_statement *prep_stmt; prep_stmt = i_new(struct sql_prepared_statement, 1); prep_stmt->db = db; prep_stmt->refcount = 1; prep_stmt->query_template = i_strdup(query_template); return prep_stmt; } static struct sql_statement * default_sql_statement_init_prepared(struct sql_prepared_statement *stmt) { return sql_statement_init(stmt->db, stmt->query_template); } const char *sql_statement_get_log_query(struct sql_statement *stmt) { if (stmt->no_log_expanded_values) return stmt->query_template; return sql_statement_get_query(stmt); } const char *sql_statement_get_query(struct sql_statement *stmt) { string_t *query = t_str_new(128); const char *const *args; unsigned int args_count, arg_pos = 0; const char *p0, *p1; args = array_get(&stmt->args, &args_count); p0 = stmt->query_template; while ((p1 = strchr(p0, '?')) != NULL) { /* append until ? */ str_append_max(query, p0, (p1 - p0)); if (arg_pos >= args_count || args[arg_pos] == NULL) { i_panic("lib-sql: Missing bind for arg #%u in statement: %s", arg_pos, stmt->query_template); } str_append(query, args[arg_pos++]); p0 = p1 + 1; } str_append(query, p0); if (arg_pos != args_count) { i_panic("lib-sql: Too many bind args (%u) for statement: %s", args_count, stmt->query_template); } return str_c(query); } static void default_sql_statement_query(struct sql_statement *stmt, sql_query_callback_t *callback, void *context) { sql_query(stmt->db, sql_statement_get_query(stmt), callback, context); pool_unref(&stmt->pool); } static struct sql_result * default_sql_statement_query_s(struct sql_statement *stmt) { struct sql_result *result = sql_query_s(stmt->db, sql_statement_get_query(stmt)); pool_unref(&stmt->pool); return result; } static void default_sql_update_stmt(struct sql_transaction_context *ctx, struct sql_statement *stmt, unsigned int *affected_rows) { ctx->db->v.update(ctx, sql_statement_get_query(stmt), affected_rows); pool_unref(&stmt->pool); } struct sql_prepared_statement * sql_prepared_statement_init(struct sql_db *db, const char *query_template) { struct sql_prepared_statement *stmt; stmt = hash_table_lookup(db->prepared_stmt_hash, query_template); if (stmt != NULL) { stmt->refcount++; return stmt; } if (db->v.prepared_statement_init != NULL) stmt = db->v.prepared_statement_init(db, query_template); else stmt = default_sql_prepared_statement_init(db, query_template); hash_table_insert(db->prepared_stmt_hash, stmt->query_template, stmt); return stmt; } void sql_prepared_statement_unref(struct sql_prepared_statement **_prep_stmt) { struct sql_prepared_statement *prep_stmt = *_prep_stmt; if (prep_stmt == NULL) return; *_prep_stmt = NULL; i_assert(prep_stmt->refcount > 0); prep_stmt->refcount--; } static void sql_statement_init_fields(struct sql_statement *stmt, struct sql_db *db) { stmt->db = db; p_array_init(&stmt->args, stmt->pool, 8); } struct sql_statement * sql_statement_init(struct sql_db *db, const char *query_template) { struct sql_statement *stmt; if (db->v.statement_init != NULL) stmt = db->v.statement_init(db, query_template); else { pool_t pool = pool_alloconly_create("sql statement", 1024); stmt = p_new(pool, struct sql_statement, 1); stmt->pool = pool; } stmt->query_template = p_strdup(stmt->pool, query_template); sql_statement_init_fields(stmt, db); return stmt; } struct sql_statement * sql_statement_init_prepared(struct sql_prepared_statement *prep_stmt) { struct sql_statement *stmt; if (prep_stmt->db->v.statement_init_prepared == NULL) return default_sql_statement_init_prepared(prep_stmt); stmt = prep_stmt->db->v.statement_init_prepared(prep_stmt); sql_statement_init_fields(stmt, prep_stmt->db); return stmt; } void sql_statement_abort(struct sql_statement **_stmt) { struct sql_statement *stmt = *_stmt; *_stmt = NULL; if (stmt->db->v.statement_abort != NULL) stmt->db->v.statement_abort(stmt); pool_unref(&stmt->pool); } void sql_statement_set_timestamp(struct sql_statement *stmt, const struct timespec *ts) { if (stmt->db->v.statement_set_timestamp != NULL) stmt->db->v.statement_set_timestamp(stmt, ts); } void sql_statement_set_no_log_expanded_values(struct sql_statement *stmt, bool no_expand) { stmt->no_log_expanded_values = no_expand; } void sql_statement_bind_str(struct sql_statement *stmt, unsigned int column_idx, const char *value) { const char *escaped_value = p_strdup_printf(stmt->pool, "'%s'", sql_escape_string(stmt->db, value)); array_idx_set(&stmt->args, column_idx, &escaped_value); if (stmt->db->v.statement_bind_str != NULL) stmt->db->v.statement_bind_str(stmt, column_idx, value); } void sql_statement_bind_binary(struct sql_statement *stmt, unsigned int column_idx, const void *value, size_t value_size) { const char *value_str = p_strdup_printf(stmt->pool, "%s", sql_escape_blob(stmt->db, value, value_size)); array_idx_set(&stmt->args, column_idx, &value_str); if (stmt->db->v.statement_bind_binary != NULL) { stmt->db->v.statement_bind_binary(stmt, column_idx, value, value_size); } } void sql_statement_bind_int64(struct sql_statement *stmt, unsigned int column_idx, int64_t value) { const char *value_str = p_strdup_printf(stmt->pool, "%"PRId64, value); array_idx_set(&stmt->args, column_idx, &value_str); if (stmt->db->v.statement_bind_int64 != NULL) stmt->db->v.statement_bind_int64(stmt, column_idx, value); } void sql_statement_bind_double(struct sql_statement *stmt, unsigned int column_idx, double value) { const char *value_str = p_strdup_printf(stmt->pool, "%f", value); array_idx_set(&stmt->args, column_idx, &value_str); if (stmt->db->v.statement_bind_double != NULL) stmt->db->v.statement_bind_double(stmt, column_idx, value); } void sql_statement_bind_uuid(struct sql_statement *stmt, unsigned int column_idx, const guid_128_t uuid) { const char *value_str = p_strdup(stmt->pool, guid_128_to_uuid_string(uuid, FORMAT_RECORD)); array_idx_set(&stmt->args, column_idx, &value_str); if (stmt->db->v.statement_bind_uuid != NULL) stmt->db->v.statement_bind_uuid(stmt, column_idx, uuid); } #undef sql_statement_query void sql_statement_query(struct sql_statement **_stmt, sql_query_callback_t *callback, void *context) { struct sql_statement *stmt = *_stmt; *_stmt = NULL; if (stmt->db->v.statement_query != NULL) stmt->db->v.statement_query(stmt, callback, context); else if (stmt->db->v.statement_query_s != NULL) { struct sql_db *db = stmt->db; struct sql_query_result_delayed *cb = i_new(struct sql_query_result_delayed, 1); cb->db = db; cb->callback = callback; cb->context = context; cb->result = sql_statement_query_s(&stmt); cb->to = timeout_add_short(0, sql_query_delayed_callback, cb); DLLIST_PREPEND(&db->query_delayed_list, cb); } else default_sql_statement_query(stmt, callback, context); } struct sql_result *sql_statement_query_s(struct sql_statement **_stmt) { struct sql_statement *stmt = *_stmt; *_stmt = NULL; if (stmt->db->v.statement_query_s != NULL) return stmt->db->v.statement_query_s(stmt); else return default_sql_statement_query_s(stmt); } void sql_result_ref(struct sql_result *result) { result->refcount++; } void sql_result_unref(struct sql_result *result) { i_assert(result->refcount > 0); if (--result->refcount > 0) return; i_free(result->map); result->v.free(result); } static const struct sql_field_def * sql_field_def_find(const struct sql_field_def *fields, const char *name) { unsigned int i; for (i = 0; fields[i].name != NULL; i++) { if (strcasecmp(fields[i].name, name) == 0) return &fields[i]; } return NULL; } static void sql_result_build_map(struct sql_result *result, const struct sql_field_def *fields, size_t dest_size) { const struct sql_field_def *def; const char *name; unsigned int i, count, field_size = 0; count = sql_result_get_fields_count(result); result->map_size = count; result->map = i_new(struct sql_field_map, result->map_size); for (i = 0; i < count; i++) { name = sql_result_get_field_name(result, i); def = sql_field_def_find(fields, name); if (def != NULL) { result->map[i].type = def->type; result->map[i].offset = def->offset; switch (def->type) { case SQL_TYPE_STR: field_size = sizeof(const char *); break; case SQL_TYPE_UINT: field_size = sizeof(unsigned int); break; case SQL_TYPE_ULLONG: field_size = sizeof(unsigned long long); break; case SQL_TYPE_BOOL: field_size = sizeof(bool); break; case SQL_TYPE_UUID: field_size = GUID_128_SIZE; break; } i_assert(def->offset + field_size <= dest_size); } else { result->map[i].offset = SIZE_MAX; } } } void sql_result_setup_fetch(struct sql_result *result, const struct sql_field_def *fields, void *dest, size_t dest_size) { if (result->map == NULL) sql_result_build_map(result, fields, dest_size); result->fetch_dest = dest; result->fetch_dest_size = dest_size; } static void sql_result_fetch(struct sql_result *result) { unsigned int i, count; const char *value; void *ptr; memset(result->fetch_dest, 0, result->fetch_dest_size); count = result->map_size; for (i = 0; i < count; i++) { if (result->map[i].offset == SIZE_MAX) continue; value = sql_result_get_field_value(result, i); ptr = PTR_OFFSET(result->fetch_dest, result->map[i].offset); switch (result->map[i].type) { case SQL_TYPE_STR: { *((const char **)ptr) = value; break; } case SQL_TYPE_UINT: { if (value != NULL && str_to_uint(value, (unsigned int *)ptr) < 0) e_error(result->event, "Value not uint: %s", value); break; } case SQL_TYPE_ULLONG: { if (value != NULL && str_to_ullong(value, (unsigned long long *)ptr) < 0) e_error(result->event, "Value not ullong: %s", value); break; } case SQL_TYPE_BOOL: { if (value != NULL && (*value == 't' || *value == '1')) *((bool *)ptr) = TRUE; break; } case SQL_TYPE_UUID: { if (value != NULL) guid_128_from_uuid_string(value, *((guid_128_t *)ptr)); break; } } } } int sql_result_next_row(struct sql_result *result) { int ret; if ((ret = result->v.next_row(result)) <= 0) return ret; if (result->fetch_dest != NULL) sql_result_fetch(result); return 1; } #undef sql_result_more void sql_result_more(struct sql_result **result, sql_query_callback_t *callback, void *context) { i_assert((*result)->v.more != NULL); (*result)->v.more(result, TRUE, callback, context); } static void sql_result_more_sync_callback(struct sql_result *result, void *context) { struct sql_result **dest_result = context; *dest_result = result; } void sql_result_more_s(struct sql_result **result) { i_assert((*result)->v.more != NULL); (*result)->v.more(result, FALSE, sql_result_more_sync_callback, result); /* the callback must have been called */ i_assert(*result != NULL); } unsigned int sql_result_get_fields_count(struct sql_result *result) { return result->v.get_fields_count(result); } const char *sql_result_get_field_name(struct sql_result *result, unsigned int idx) { return result->v.get_field_name(result, idx); } int sql_result_find_field(struct sql_result *result, const char *field_name) { return result->v.find_field(result, field_name); } const char *sql_result_get_field_value(struct sql_result *result, unsigned int idx) { return result->v.get_field_value(result, idx); } const unsigned char * sql_result_get_field_value_binary(struct sql_result *result, unsigned int idx, size_t *size_r) { return result->v.get_field_value_binary(result, idx, size_r); } const char *sql_result_find_field_value(struct sql_result *result, const char *field_name) { return result->v.find_field_value(result, field_name); } const char *const *sql_result_get_values(struct sql_result *result) { return result->v.get_values(result); } const char *sql_result_get_error(struct sql_result *result) { return result->v.get_error(result); } enum sql_result_error_type sql_result_get_error_type(struct sql_result *result) { return result->error_type; } static void sql_result_not_connected_free(struct sql_result *result ATTR_UNUSED) { } static int sql_result_not_connected_next_row(struct sql_result *result ATTR_UNUSED) { return -1; } static const char * sql_result_not_connected_get_error(struct sql_result *result ATTR_UNUSED) { return SQL_ERRSTR_NOT_CONNECTED; } struct sql_transaction_context *sql_transaction_begin(struct sql_db *db) { return db->v.transaction_begin(db); } void sql_transaction_set_non_atomic(struct sql_transaction_context *ctx) { ctx->non_atomic = TRUE; } static void sql_commit_delayed_callback(struct sql_commit_result_delayed *cb) { struct sql_commit_result result = { .error = cb->error, }; timeout_remove(&cb->to); DLLIST_REMOVE(&cb->db->commit_delayed_list, cb); cb->callback(&result, cb->context); i_free(cb->error); i_free(cb); } #undef sql_transaction_commit void sql_transaction_commit(struct sql_transaction_context **_ctx, sql_commit_callback_t *callback, void *context) { struct sql_transaction_context *ctx = *_ctx; struct sql_db *db = ctx->db; *_ctx = NULL; if (ctx->db->v.transaction_commit != NULL) { ctx->db->v.transaction_commit(ctx, callback, context); return; } struct sql_commit_result_delayed *cb = i_new(struct sql_commit_result_delayed, 1); const char *error = NULL; ctx->db->v.transaction_commit_s(ctx, &error); cb->db = db; cb->error = i_strdup(error); cb->callback = callback; cb->context = context; cb->to = timeout_add_short(0, sql_commit_delayed_callback, cb); DLLIST_PREPEND(&db->commit_delayed_list, cb); } int sql_transaction_commit_s(struct sql_transaction_context **_ctx, const char **error_r) { struct sql_transaction_context *ctx = *_ctx; *_ctx = NULL; return ctx->db->v.transaction_commit_s(ctx, error_r); } void sql_transaction_rollback(struct sql_transaction_context **_ctx) { struct sql_transaction_context *ctx = *_ctx; *_ctx = NULL; ctx->db->v.transaction_rollback(ctx); } void sql_update(struct sql_transaction_context *ctx, const char *query) { ctx->db->v.update(ctx, query, NULL); } void sql_update_stmt(struct sql_transaction_context *ctx, struct sql_statement **_stmt) { struct sql_statement *stmt = *_stmt; *_stmt = NULL; if (ctx->db->v.update_stmt != NULL) ctx->db->v.update_stmt(ctx, stmt, NULL); else default_sql_update_stmt(ctx, stmt, NULL); } void sql_update_get_rows(struct sql_transaction_context *ctx, const char *query, unsigned int *affected_rows) { ctx->db->v.update(ctx, query, affected_rows); } void sql_update_stmt_get_rows(struct sql_transaction_context *ctx, struct sql_statement **_stmt, unsigned int *affected_rows) { struct sql_statement *stmt = *_stmt; *_stmt = NULL; if (ctx->db->v.update_stmt != NULL) ctx->db->v.update_stmt(ctx, stmt, affected_rows); else default_sql_update_stmt(ctx, stmt, affected_rows); } void sql_db_set_state(struct sql_db *db, enum sql_db_state state) { enum sql_db_state old_state = db->state; if (db->state == state) return; db->state = state; if (db->state_change_callback != NULL) { db->state_change_callback(db, old_state, db->state_change_context); } } void sql_transaction_add_query(struct sql_transaction_context *ctx, pool_t pool, const char *query, unsigned int *affected_rows) { struct sql_transaction_query *tquery; tquery = p_new(pool, struct sql_transaction_query, 1); tquery->trans = ctx; tquery->query = p_strdup(pool, query); tquery->affected_rows = affected_rows; if (ctx->head == NULL) ctx->head = tquery; else ctx->tail->next = tquery; ctx->tail = tquery; } void sql_connection_log_finished(struct sql_db *db) { struct event_passthrough *e = event_create_passthrough(db->event)-> set_name(SQL_CONNECTION_FINISHED)-> add_str("name", db->name)-> add_str("error", db->last_connect_error); e_debug(e->event(), "Connection finished (queries=%"PRIu64", slow queries=%"PRIu64")", db->succeeded_queries + db->failed_queries, db->slow_queries); i_free(db->last_connect_error); } struct event_passthrough * sql_query_finished_event(struct sql_db *db, struct event *event, const char *query, bool success, int *duration_r) { long long diff; struct timeval tv, tv2; event_get_create_time(event, &tv); i_gettimeofday(&tv2); struct event_passthrough *e = event_create_passthrough(event)-> set_name(SQL_QUERY_FINISHED)-> add_str("query_first_word", t_strcut(query, ' ')); diff = timeval_diff_msecs(&tv2, &tv); if (!success) { db->failed_queries++; } else { db->succeeded_queries++; } if (diff >= SQL_SLOW_QUERY_MSEC) { e->add_str("slow_query", "y"); db->slow_queries++; } i_assert(diff <= INT_MAX); *duration_r = (int)diff; return e; } struct event_passthrough *sql_transaction_finished_event(struct sql_transaction_context *ctx) { return event_create_passthrough(ctx->event)-> set_name(SQL_TRANSACTION_FINISHED); } void sql_wait(struct sql_db *db) { if (db->v.wait != NULL) db->v.wait(db); sql_call_delayed_callbacks(db); } struct sql_result sql_not_connected_result = { .v = { sql_result_not_connected_free, sql_result_not_connected_next_row, NULL, NULL, NULL, NULL, NULL, NULL, NULL, sql_result_not_connected_get_error, NULL, }, .failed_try_retry = TRUE };