From 8104f6f228c822857b7f90afba9d207ef52028ae Mon Sep 17 00:00:00 2001 From: Cory McWilliams Date: Sat, 12 Apr 2025 08:40:36 -0400 Subject: [PATCH] ssb: Fix and test the messages_stats trigger. --- src/ssb.db.c | 11 +++++-- src/ssb.tests.c | 87 ++++++++++++++++++++++++++++++++++++++++++++++++- src/ssb.tests.h | 6 ++++ src/tests.c | 1 + 4 files changed, 102 insertions(+), 3 deletions(-) diff --git a/src/ssb.db.c b/src/ssb.db.c index 5b6d2226..bd6646af 100644 --- a/src/ssb.db.c +++ b/src/ssb.db.c @@ -150,9 +150,16 @@ void tf_ssb_db_init(tf_ssb_t* ssb) "CREATE TRIGGER IF NOT EXISTS messages_ai_stats AFTER INSERT ON messages BEGIN INSERT INTO messages_stats(author, max_sequence, max_timestamp) VALUES (new.author, " "new.sequence, new.timestamp) ON CONFLICT DO UPDATE SET max_sequence = MAX(max_sequence, new.sequence), max_timestamp = MAX(max_timestamp, " "new.timestamp); END"); + _tf_ssb_db_exec(db, "DROP TRIGGER IF EXISTS messages_ad_stats"); _tf_ssb_db_exec(db, - "CREATE TRIGGER IF NOT EXISTS messages_ad_stats AFTER DELETE ON messages BEGIN UPDATE messages_stats SET max_sequence = (SELECT MAX(messages.sequence) FROM messages WHERE " - "messages.author = old.author), max_timestamp = (SELECT MAX(messages.timestamp) FROM messages WHERE messages.author = old.author); END"); + "CREATE TRIGGER IF NOT EXISTS messages_ad_stats AFTER DELETE ON messages BEGIN " + "UPDATE messages_stats SET max_sequence = updated.sequence, max_timestamp = updated.timestamp " + "FROM (" + " SELECT COALESCE(MAX(messages.sequence), 0) AS sequence, COALESCE(MAX(messages.timestamp), 0) AS timestamp " + " FROM messages WHERE messages.author = old.author) AS updated " + "WHERE messages_stats.author = old.author; " + "DELETE FROM messages_stats WHERE messages_stats.author = old.author AND messages_stats.max_sequence = 0; " + "END"); if (_tf_ssb_db_has_rows(db, "SELECT name FROM pragma_table_info('messages') WHERE name = 'content' AND type == 'TEXT'")) { diff --git a/src/ssb.tests.c b/src/ssb.tests.c index 191d4186..53d39d62 100644 --- a/src/ssb.tests.c +++ b/src/ssb.tests.c @@ -18,6 +18,8 @@ #include "sodium/crypto_sign.h" +#include "sqlite3.h" + #if defined(__APPLE__) #include #endif @@ -659,7 +661,7 @@ void tf_ssb_test_following(const tf_test_options_t* options) void tf_ssb_test_bench(const tf_test_options_t* options) { - tf_printf("Testing following.\n"); + tf_printf("Testing bench.\n"); uv_loop_t loop = { 0 }; uv_loop_init(&loop); @@ -1393,4 +1395,87 @@ void tf_ssb_test_invite(const tf_test_options_t* options) uv_loop_close(&loop); } +void tf_ssb_test_triggers(const tf_test_options_t* options) +{ + tf_printf("Testing triggers.\n"); + + uv_loop_t loop = { 0 }; + uv_loop_init(&loop); + + tf_trace_t* trace = tf_trace_create(); + + unlink("out/test_db0.sqlite"); + tf_ssb_t* ssb0 = tf_ssb_create(&loop, NULL, "file:out/test_db0.sqlite", NULL); + tf_ssb_set_trace(ssb0, trace); + tf_ssb_generate_keys(ssb0); + + char id0[k_id_base64_len] = { 0 }; + tf_ssb_whoami(ssb0, id0, sizeof(id0)); + + uint8_t priv0[512]; + tf_ssb_get_private_key(ssb0, priv0, sizeof(priv0)); + + struct timespec start_time = { 0 }; + struct timespec end_time = { 0 }; + clock_gettime(CLOCK_REALTIME, &start_time); + const int k_messages = 5; + JSValue obj = JS_NewObject(tf_ssb_get_context(ssb0)); + JS_SetPropertyStr(tf_ssb_get_context(ssb0), obj, "type", JS_NewString(tf_ssb_get_context(ssb0), "post")); + JS_SetPropertyStr(tf_ssb_get_context(ssb0), obj, "text", JS_NewString(tf_ssb_get_context(ssb0), "Hello, world!")); + for (int i = 0; i < k_messages; i++) + { + bool stored = false; + JSValue signed_message = tf_ssb_sign_message(ssb0, id0, priv0, obj, NULL, 0); + tf_ssb_verify_strip_and_store_message(ssb0, signed_message, _message_stored, &stored); + JS_FreeValue(tf_ssb_get_context(ssb0), signed_message); + _wait_stored(ssb0, &stored); + } + JS_FreeValue(tf_ssb_get_context(ssb0), obj); + clock_gettime(CLOCK_REALTIME, &end_time); + tf_printf("insert = %f seconds\n", (end_time.tv_sec - start_time.tv_sec) + (end_time.tv_nsec - start_time.tv_nsec) / 1e9); + + int64_t max_sequence = 0; + tf_ssb_db_get_latest_message_by_author(ssb0, id0, &max_sequence, NULL, 0); + tf_printf("max_sequence=%" PRId64 "\n", max_sequence); + assert(max_sequence == 5); + + sqlite3* db = tf_ssb_acquire_db_writer(ssb0); + sqlite3_exec(db, "DELETE FROM messages WHERE sequence = 5", NULL, NULL, NULL); + tf_ssb_release_db_writer(ssb0, db); + + max_sequence = 0; + tf_ssb_db_get_latest_message_by_author(ssb0, id0, &max_sequence, NULL, 0); + tf_printf("max_sequence=%" PRId64 "\n", max_sequence); + assert(max_sequence == 4); + + tf_ssb_acquire_db_writer(ssb0); + sqlite3_exec(db, "DELETE FROM messages", NULL, NULL, NULL); + tf_ssb_release_db_writer(ssb0, db); + + max_sequence = 0; + tf_ssb_db_get_latest_message_by_author(ssb0, id0, &max_sequence, NULL, 0); + tf_printf("max_sequence=%" PRId64 "\n", max_sequence); + assert(max_sequence == 0); + + uv_run(&loop, UV_RUN_DEFAULT); + + char* trace_data = tf_trace_export(trace); + if (trace_data) + { + FILE* file = fopen("out/trace.json", "wb"); + if (file) + { + fwrite(trace_data, 1, strlen(trace_data), file); + fclose(file); + } + tf_free(trace_data); + } + + tf_ssb_destroy(ssb0); + + tf_trace_destroy(trace); + + uv_loop_close(&loop); +} + #endif diff --git a/src/ssb.tests.h b/src/ssb.tests.h index 2e414bcf..045e0eb7 100644 --- a/src/ssb.tests.h +++ b/src/ssb.tests.h @@ -83,4 +83,10 @@ void tf_ssb_test_connect_str(const tf_test_options_t* options); */ void tf_ssb_test_invite(const tf_test_options_t* options); +/** +** Test triggers. +** @param options The test options. +*/ +void tf_ssb_test_triggers(const tf_test_options_t* options); + /** @} */ diff --git a/src/tests.c b/src/tests.c index 505925da..bc803dcb 100644 --- a/src/tests.c +++ b/src/tests.c @@ -1078,6 +1078,7 @@ void tf_tests(const tf_test_options_t* options) _tf_test_run(options, "replicate", tf_ssb_test_replicate, false); _tf_test_run(options, "connect_str", tf_ssb_test_connect_str, false); _tf_test_run(options, "invite", tf_ssb_test_invite, false); + _tf_test_run(options, "triggers", tf_ssb_test_triggers, false); tf_printf("Tests completed.\n"); #endif }