Skip to content

Commit a8f7255

Browse files
authored
H2O: Use prepared statements for the database updates test (TechEmpower#9124)
1 parent 635f068 commit a8f7255

File tree

3 files changed

+123
-66
lines changed

3 files changed

+123
-66
lines changed

frameworks/C/h2o/src/database.c

+6-4
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,8 @@ typedef struct {
5656

5757
typedef struct {
5858
list_t l;
59-
const char *name;
60-
const char *query;
59+
char *name;
60+
char *query;
6161
} prepared_statement_t;
6262

6363
static h2o_socket_t *create_socket(int sd, h2o_loop_t *loop);
@@ -713,8 +713,8 @@ void add_prepared_statement(const char *name, const char *query, list_t **prepar
713713

714714
memset(p, 0, sizeof(*p));
715715
p->l.next = *prepared_statements;
716-
p->name = name;
717-
p->query = query;
716+
p->name = h2o_strdup(NULL, name, SIZE_MAX).base;
717+
p->query = h2o_strdup(NULL, query, SIZE_MAX).base;
718718
*prepared_statements = &p->l;
719719
}
720720

@@ -791,6 +791,8 @@ void remove_prepared_statements(list_t *prepared_statements)
791791
prepared_statements);
792792

793793
prepared_statements = prepared_statements->next;
794+
free(p->name);
795+
free(p->query);
794796
free(p);
795797
} while (prepared_statements);
796798
}

frameworks/C/h2o/src/database.h

+2-2
Original file line numberDiff line numberDiff line change
@@ -49,10 +49,10 @@ typedef struct db_query_param_t {
4949
on_result_t on_result;
5050
void (*on_timeout)(struct db_query_param_t *);
5151
const char *command;
52-
const char * const *paramValues;
53-
const int *paramLengths;
5452
const int *paramFormats;
53+
const int *paramLengths;
5554
const Oid *paramTypes;
55+
const char * const *paramValues;
5656
size_t nParams;
5757
uint_fast32_t flags;
5858
int resultFormat;

frameworks/C/h2o/src/handlers/world.c

+115-60
Original file line numberDiff line numberDiff line change
@@ -57,17 +57,18 @@
5757
// MAX_UPDATE_QUERY_LEN must be updated whenever UPDATE_QUERY_BEGIN, UPDATE_QUERY_ELEM,
5858
// UPDATE_QUERY_MIDDLE, UPDATE_QUERY_ELEM2, and UPDATE_QUERY_END are changed.
5959
#define UPDATE_QUERY_BEGIN "UPDATE " WORLD_TABLE_NAME " SET randomNumber = CASE id "
60-
#define UPDATE_QUERY_ELEM "WHEN %" PRIu32 " THEN %" PRIu32 " "
61-
#define UPDATE_QUERY_MIDDLE "ELSE randomNumber END WHERE id IN (%" PRIu32
62-
#define UPDATE_QUERY_ELEM2 ",%" PRIu32
60+
#define UPDATE_QUERY_ELEM "WHEN $%zu::integer THEN $%zu::integer "
61+
#define UPDATE_QUERY_MIDDLE "ELSE randomNumber END WHERE id IN ($1::integer"
62+
#define UPDATE_QUERY_ELEM2 ",$%zu::integer"
6363
#define UPDATE_QUERY_END ");"
6464

6565
#define MAX_UPDATE_QUERY_LEN(n) \
6666
(sizeof(UPDATE_QUERY_BEGIN) + sizeof(UPDATE_QUERY_MIDDLE) + \
6767
sizeof(UPDATE_QUERY_END) - 1 - sizeof(UPDATE_QUERY_ELEM2) + \
6868
(n) * (sizeof(UPDATE_QUERY_ELEM) - 1 + sizeof(UPDATE_QUERY_ELEM2) - 1 + \
69-
3 * (sizeof(MKSTR(MAX_ID)) - 1) - 3 * (sizeof(PRIu32) - 1) - 3))
69+
3 * sizeof(MKSTR(MAX_QUERIES)) - 3 * (sizeof("%zu") - 1)))
7070

71+
#define UPDATE_QUERY_NAME_PREFIX WORLD_TABLE_NAME "Update"
7172
#define USE_CACHE 2
7273
#define WORLD_QUERY "SELECT * FROM " WORLD_TABLE_NAME " WHERE id = $1::integer;"
7374

@@ -259,11 +260,16 @@ static int do_multiple_queries(bool do_update, bool use_cache, h2o_req_t *req)
259260
size_t sz = base_size + num_query_in_progress * sizeof(query_param_t);
260261

261262
if (do_update) {
262-
const size_t reuse_size = (num_query_in_progress - 1) * sizeof(query_param_t);
263-
const size_t update_query_len = MAX_UPDATE_QUERY_LEN(num_query);
263+
size_t s = base_size + sizeof(query_param_t);
264264

265-
if (update_query_len > reuse_size)
266-
sz += update_query_len - reuse_size;
265+
s = (s + _Alignof(const char *) - 1) / _Alignof(const char *);
266+
s = s * _Alignof(const char *) + 2 * num_query * sizeof(const char *);
267+
s = (s + _Alignof(int) - 1) / _Alignof(int);
268+
s = s * _Alignof(int) + 4 * num_query * sizeof(int);
269+
s += sizeof(UPDATE_QUERY_NAME_PREFIX MKSTR(MAX_QUERIES));
270+
271+
if (s > sz)
272+
sz = s;
267273
}
268274

269275
multiple_query_ctx_t * const query_ctx = h2o_mem_alloc(sz);
@@ -332,65 +338,57 @@ static int do_multiple_queries(bool do_update, bool use_cache, h2o_req_t *req)
332338

333339
static void do_updates(multiple_query_ctx_t *query_ctx)
334340
{
335-
char *iter = (char *) (query_ctx->query_param + 1);
336-
size_t sz = MAX_UPDATE_QUERY_LEN(query_ctx->num_result);
337-
338-
// Sort the results to avoid database deadlock.
339-
qsort(query_ctx->res, query_ctx->num_result, sizeof(*query_ctx->res), compare_items);
340-
query_ctx->query_param->param.command = iter;
341-
query_ctx->query_param->param.nParams = 0;
342-
query_ctx->query_param->param.on_result = on_update_result;
343-
query_ctx->query_param->param.paramFormats = NULL;
344-
query_ctx->query_param->param.paramLengths = NULL;
345-
query_ctx->query_param->param.paramValues = NULL;
346-
query_ctx->query_param->param.flags = 0;
341+
size_t offset =
342+
offsetof(multiple_query_ctx_t, res) + query_ctx->num_result * sizeof(*query_ctx->res);
347343

348-
int c = snprintf(iter, sz, UPDATE_QUERY_BEGIN);
349-
350-
if ((size_t) c >= sz)
351-
goto error;
352-
353-
iter += c;
354-
sz -= c;
355-
356-
for (size_t i = 0; i < query_ctx->num_result; i++) {
357-
query_ctx->res[i].random_number = 1 + get_random_number(MAX_ID,
358-
&query_ctx->ctx->random_seed);
359-
c = snprintf(iter,
360-
sz,
361-
UPDATE_QUERY_ELEM,
362-
query_ctx->res[i].id,
363-
query_ctx->res[i].random_number);
344+
offset = ((offset + _Alignof(query_param_t) - 1) / _Alignof(query_param_t));
345+
offset = offset * _Alignof(query_param_t) + sizeof(query_param_t);
346+
offset = (offset + _Alignof(const char *) - 1) / _Alignof(const char *);
347+
offset *= _Alignof(const char *);
364348

365-
if ((size_t) c >= sz)
366-
goto error;
349+
const char ** const paramValues = (const char **) ((char *) query_ctx + offset);
350+
const size_t nParams = query_ctx->num_result * 2;
367351

368-
iter += c;
369-
sz -= c;
370-
}
352+
offset += nParams * sizeof(*paramValues);
353+
offset = (offset + _Alignof(int) - 1) / _Alignof(int);
354+
offset *= _Alignof(int);
371355

372-
c = snprintf(iter, sz, UPDATE_QUERY_MIDDLE, query_ctx->res->id);
356+
int * const paramFormats = (int *) ((char *) query_ctx + offset);
357+
int * const paramLengths = paramFormats + nParams;
358+
char * const command = (char *) (paramLengths + nParams);
359+
const size_t command_size =
360+
sizeof(UPDATE_QUERY_NAME_PREFIX MKSTR(MAX_QUERIES)) - sizeof(UPDATE_QUERY_NAME_PREFIX) + 1;
361+
const int c = snprintf(command + sizeof(UPDATE_QUERY_NAME_PREFIX) - 1,
362+
command_size,
363+
"%zu",
364+
query_ctx->num_result);
373365

374-
if ((size_t) c >= sz)
366+
if ((size_t) c >= command_size)
375367
goto error;
376368

377-
iter += c;
378-
sz -= c;
379-
380-
for (size_t i = 1; i < query_ctx->num_result; i++) {
381-
c = snprintf(iter, sz, UPDATE_QUERY_ELEM2, query_ctx->res[i].id);
382-
383-
if ((size_t) c >= sz)
384-
goto error;
385-
386-
iter += c;
387-
sz -= c;
388-
}
389-
390-
c = snprintf(iter, sz, UPDATE_QUERY_END);
369+
memcpy(command, UPDATE_QUERY_NAME_PREFIX, sizeof(UPDATE_QUERY_NAME_PREFIX) - 1);
370+
// Sort the results to avoid database deadlock.
371+
qsort(query_ctx->res, query_ctx->num_result, sizeof(*query_ctx->res), compare_items);
391372

392-
if ((size_t) c >= sz)
393-
goto error;
373+
for (size_t i = 0; i < query_ctx->num_result; i++) {
374+
query_ctx->res[i].id = htonl(query_ctx->res[i].id);
375+
query_ctx->res[i].random_number =
376+
htonl(1 + get_random_number(MAX_ID, &query_ctx->ctx->random_seed));
377+
paramFormats[2 * i] = 1;
378+
paramFormats[2 * i + 1] = 1;
379+
paramLengths[2 * i] = sizeof(query_ctx->res[i].id);
380+
paramLengths[2 * i + 1] = sizeof(query_ctx->res[i].random_number);
381+
paramValues[2 * i] = (const char *) &query_ctx->res[i].id;
382+
paramValues[2 * i + 1] = (const char *) &query_ctx->res[i].random_number;
383+
}
384+
385+
query_ctx->query_param->param.command = command;
386+
query_ctx->query_param->param.flags = IS_PREPARED;
387+
query_ctx->query_param->param.nParams = nParams;
388+
query_ctx->query_param->param.on_result = on_update_result;
389+
query_ctx->query_param->param.paramFormats = paramFormats;
390+
query_ctx->query_param->param.paramLengths = paramLengths;
391+
query_ctx->query_param->param.paramValues = paramValues;
394392

395393
if (execute_database_query(&query_ctx->ctx->request_handler_data.hello_world_db,
396394
&query_ctx->query_param->param)) {
@@ -726,8 +724,14 @@ static result_return_t on_update_result(db_query_param_t *param, PGresult *resul
726724
query_ctx->gen = get_json_generator(&query_ctx->ctx->json_generator,
727725
&query_ctx->ctx->json_generator_num);
728726

729-
if (query_ctx->gen)
727+
if (query_ctx->gen) {
728+
for (size_t i = 0; i < query_ctx->num_result; i++) {
729+
query_ctx->res[i].id = ntohl(query_ctx->res[i].id);
730+
query_ctx->res[i].random_number = ntohl(query_ctx->res[i].random_number);
731+
}
732+
730733
serialize_items(query_ctx->res, query_ctx->num_result, &query_ctx->gen, query_ctx->req);
734+
}
731735
else
732736
send_error(INTERNAL_SERVER_ERROR, REQ_ERROR, query_ctx->req);
733737
}
@@ -887,6 +891,57 @@ void initialize_world_handlers(h2o_hostconf_t *hostconf,
887891
h2o_access_log_filehandle_t *log_handle,
888892
request_handler_data_t *data)
889893
{
894+
char name[sizeof(UPDATE_QUERY_NAME_PREFIX MKSTR(MAX_QUERIES))];
895+
char query[MAX_UPDATE_QUERY_LEN(MAX_QUERIES)];
896+
const size_t name_size = sizeof(name) - sizeof(UPDATE_QUERY_NAME_PREFIX) + 1;
897+
898+
assert(sizeof(name) >= sizeof(UPDATE_QUERY_NAME_PREFIX));
899+
memcpy(name, UPDATE_QUERY_NAME_PREFIX, sizeof(UPDATE_QUERY_NAME_PREFIX) - 1);
900+
assert(sizeof(query) >= sizeof(UPDATE_QUERY_BEGIN));
901+
memcpy(query, UPDATE_QUERY_BEGIN, sizeof(UPDATE_QUERY_BEGIN) - 1);
902+
903+
for (size_t i = 0; i < MAX_QUERIES; i++) {
904+
char *iter = query + sizeof(UPDATE_QUERY_BEGIN) - 1;
905+
size_t sz = sizeof(query) - sizeof(UPDATE_QUERY_BEGIN) + 1;
906+
int c = snprintf(name + sizeof(UPDATE_QUERY_NAME_PREFIX) - 1, name_size, "%zu", i + 1);
907+
908+
if ((size_t) c >= name_size)
909+
continue;
910+
911+
for (size_t j = 0; j <= i; j++) {
912+
c = snprintf(iter,
913+
sz,
914+
UPDATE_QUERY_ELEM,
915+
2 * j + 1,
916+
2 * j + 2);
917+
918+
if ((size_t) c >= sz)
919+
continue;
920+
921+
iter += c;
922+
sz -= c;
923+
}
924+
925+
assert(sz >= sizeof(UPDATE_QUERY_MIDDLE));
926+
memcpy(iter, UPDATE_QUERY_MIDDLE, sizeof(UPDATE_QUERY_MIDDLE) - 1);
927+
iter += sizeof(UPDATE_QUERY_MIDDLE) - 1;
928+
sz -= sizeof(UPDATE_QUERY_MIDDLE) - 1;
929+
930+
for (size_t j = 1; j <= i; j++) {
931+
c = snprintf(iter, sz, UPDATE_QUERY_ELEM2, 2 * j + 1);
932+
933+
if ((size_t) c >= sz)
934+
continue;
935+
936+
iter += c;
937+
sz -= c;
938+
}
939+
940+
assert(sz >= sizeof(UPDATE_QUERY_END));
941+
memcpy(iter, UPDATE_QUERY_END, sizeof(UPDATE_QUERY_END));
942+
add_prepared_statement(name, query, &data->prepared_statements);
943+
}
944+
890945
add_prepared_statement(WORLD_TABLE_NAME, WORLD_QUERY, &data->prepared_statements);
891946
register_request_handler("/cached-worlds", cached_queries, hostconf, log_handle);
892947
register_request_handler("/db", single_query, hostconf, log_handle);

0 commit comments

Comments
 (0)