From 98282f1a02d29d6fdd0b3db63ae398d0162fe7bd Mon Sep 17 00:00:00 2001 From: Aki Tuomi Date: Mon, 18 Aug 2025 08:30:42 +0300 Subject: [PATCH] lib-sql: Use strchr() based while loop to fill template This is much faster than going one byte at a time. --- src/lib-sql/sql-api.c | 26 ++++++++++++++------------ src/lib-sql/test-sql.c | 30 ++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 12 deletions(-) diff --git a/src/lib-sql/sql-api.c b/src/lib-sql/sql-api.c index 1bdc5599e5..148ff84dd6 100644 --- a/src/lib-sql/sql-api.c +++ b/src/lib-sql/sql-api.c @@ -292,22 +292,24 @@ const char *sql_statement_get_query(struct sql_statement *stmt) { string_t *query = t_str_new(128); const char *const *args; - unsigned int i, args_count, arg_pos = 0; + unsigned int args_count, arg_pos = 0; + const char *p0, *p1; args = array_get(&stmt->args, &args_count); - - for (i = 0; stmt->query_template[i] != '\0'; i++) { - if (stmt->query_template[i] == '?') { - 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++]); - } else { - str_append_c(query, stmt->query_template[i]); + 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); diff --git a/src/lib-sql/test-sql.c b/src/lib-sql/test-sql.c index f4a432efcd..3945268e40 100644 --- a/src/lib-sql/test-sql.c +++ b/src/lib-sql/test-sql.c @@ -60,6 +60,24 @@ static void deinit_sql(struct sql_db **_sql) }; \ sql_driver_test_add_expected_result(sql, &result_1); +#define setup_result_2(sql) \ + struct test_driver_result_set rset_2 = { \ + .rows = 2, \ + .cols = 1, \ + .col_names = (const char *[]){"foo", NULL}, \ + .row_data = (const char **[]){ \ + (const char*[]){"value1", NULL}, \ + (const char*[]){"value2", NULL}, \ + }, \ + }; \ + struct test_driver_result result_2 = { \ + .nqueries = 1, \ + .queries = (const char *[]){"SELECT foo FROM bar WHERE baz = 'foz'"}, \ + .result = &rset_2 \ + }; \ + sql_driver_test_add_expected_result(sql, &result_2); + + static void test_result_1(struct sql_result *cursor) { test_assert(sql_result_next_row(cursor) == SQL_RESULT_NEXT_OK); @@ -127,6 +145,18 @@ static void test_sql_stmt_prepared_api(void) sql_result_unref(cursor); + setup_result_2(sql); + + prep_stmt = sql_prepared_statement_init(sql, "SELECT foo FROM bar WHERE baz = ?"); + stmt = sql_statement_init_prepared(prep_stmt); + sql_statement_bind_str(stmt, 0, "foz"); + sql_prepared_statement_unref(&prep_stmt); + cursor = sql_statement_query_s(&stmt); + + test_result_1(cursor); + + sql_result_unref(cursor); + deinit_sql(&sql); test_end(); } -- 2.47.2