no-DB mode implementation #94

Manually merged
jabber.developer merged 34 commits from feat/no-db-mode into master 2026-05-05 19:32:42 +00:00
31 changed files with 6192 additions and 677 deletions
Showing only changes of commit 06463b75b4 - Show all commits

View File

@@ -379,7 +379,7 @@ log_database_export_to_flatfile(const gchar* const contact_jid)
g_slist_free_full(merged, (GDestroyNotify)ff_parsed_line_free);
fflush(fp);
fsync(fd); // ensure data is on disk before atomic rename
fsync(fd); // ensure data is on disk before atomic rename
fclose(fp); // also releases flock
// Atomic rename

View File

@@ -55,7 +55,7 @@
static sqlite3* g_chatlog_database;
static void _add_to_db(ProfMessage* message, char* type, const Jid* const from_jid, const Jid* const to_jid);
static void _add_to_db(ProfMessage* message, const char* type, const Jid* const from_jid, const Jid* const to_jid);
static char* _get_db_filename(ProfAccount* account);
static prof_msg_type_t _get_message_type_type(const char* const type);
static prof_enc_t _get_message_enc_type(const char* const encstr);
@@ -147,22 +147,22 @@ _sqlite_init(ProfAccount* account)
return TRUE;
jabber.developer marked this conversation as resolved
Review

We might want to also check integrity of the tables (whether they exist + maybe even their columns). But it would introduce further complexity and should be a subject of a separate issue.

We might want to also check integrity of the tables (whether they exist + maybe even their columns). But it would introduce further complexity and should be a subject of a separate issue.

Postponed

Postponed
}
char* query = "CREATE TABLE IF NOT EXISTS `ChatLogs` ("
"`id` INTEGER PRIMARY KEY AUTOINCREMENT, "
"`from_jid` TEXT NOT NULL, "
"`to_jid` TEXT NOT NULL, "
"`from_resource` TEXT, "
"`to_resource` TEXT, "
"`message` TEXT, "
"`timestamp` TEXT, "
"`type` TEXT, "
"`stanza_id` TEXT, "
"`archive_id` TEXT, "
"`encryption` TEXT, "
"`marked_read` INTEGER, "
"`replace_id` TEXT, "
"`replaces_db_id` INTEGER, "
"`replaced_by_db_id` INTEGER)";
const char* query = "CREATE TABLE IF NOT EXISTS `ChatLogs` ("
jabber.developer marked this conversation as resolved Outdated

Here and in other places, can we return necessary documentation detailing DB fields, decisions, and other useful points?

Here and in other places, can we return necessary documentation detailing DB fields, decisions, and other useful points?

Corrected

Corrected
"`id` INTEGER PRIMARY KEY AUTOINCREMENT, "
"`from_jid` TEXT NOT NULL, "
"`to_jid` TEXT NOT NULL, "
"`from_resource` TEXT, "
"`to_resource` TEXT, "
"`message` TEXT, "
"`timestamp` TEXT, "
"`type` TEXT, "
"`stanza_id` TEXT, "
"`archive_id` TEXT, "
"`encryption` TEXT, "
"`marked_read` INTEGER, "
"`replace_id` TEXT, "
"`replaces_db_id` INTEGER, "
"`replaced_by_db_id` INTEGER)";
if (SQLITE_OK != sqlite3_exec(g_chatlog_database, query, NULL, 0, &err_msg)) {
goto out;
}
@@ -251,7 +251,7 @@ _sqlite_add_incoming(ProfMessage* message)
}
static void
_log_database_add_outgoing(char* type, const char* const id, const char* const barejid, const char* const message, const char* const replace_id, prof_enc_t enc)
_log_database_add_outgoing(const char* type, const char* const id, const char* const barejid, const char* const message, const char* const replace_id, prof_enc_t enc)
{
ProfMessage* msg = message_init();
@@ -350,8 +350,8 @@ _sqlite_get_previous_chat(const gchar* const contact_barejid, const gchar* start
return DB_RESPONSE_ERROR;
}
gchar* sort1 = from_start ? "ASC" : "DESC";
gchar* sort2 = !flip ? "ASC" : "DESC";
const gchar* sort1 = from_start ? "ASC" : "DESC";
const gchar* sort2 = !flip ? "ASC" : "DESC";
GDateTime* now = g_date_time_new_now_local();
auto_gchar gchar* end_date_fmt = end_time ? g_strdup(end_time) : g_date_time_format_iso8601(now);
auto_sqlite gchar* query = sqlite3_mprintf("SELECT * FROM ("
@@ -571,7 +571,7 @@ _get_message_enc_type(const char* const encstr)
// --- Core write logic ---
static void
_add_to_db(ProfMessage* message, char* type, const Jid* const from_jid, const Jid* const to_jid)
_add_to_db(ProfMessage* message, const char* type, const Jid* const from_jid, const Jid* const to_jid)
{
auto_gchar gchar* pref_dblog = prefs_get_string(PREF_DBLOG);
sqlite_int64 original_message_id = -1;

View File

@@ -66,7 +66,7 @@
int
main(int argc, char* argv[])
{
int group = 0; /* 0 = all groups */
int group = 0; /* 0 = all groups */
if (argc > 1) {
group = atoi(argv[1]);
if (group < 1 || group > 4) {
@@ -79,7 +79,7 @@ main(int argc, char* argv[])
char group_env[16];
snprintf(group_env, sizeof(group_env), "%d", group);
setenv("PROF_TEST_GROUP", group_env, 1);
fprintf(stderr, "[PROF_TEST] Starting functional tests, group=%d\n", group);
/* ============================================================
@@ -268,7 +268,8 @@ main(int argc, char* argv[])
};
/* Test group registry for easy extension */
struct {
struct
{
const char* name;
const struct CMUnitTest* tests;
size_t count;

View File

@@ -377,11 +377,6 @@ init_prof_test(void **state)
"[notifications]\n"
"message=false\n"
"room=false\n");
const char *flatfile_env = getenv("PROF_FLATFILE");
if (flatfile_env && strcmp(flatfile_env, "1") == 0) {
fprintf(prc, "[logging]\ndblog=flatfile\n");
printf("[PROF_TEST] Wrote profrc with dblog=flatfile: %s\n", profrc_path);
}
fclose(prc);
}
@@ -441,17 +436,33 @@ prof_stop(void)
{
if (fd > 0 && child_pid > 0) {
prof_input("/quit");
sleep(1);
waitpid(child_pid, NULL, 0);
sleep_ms(QUIT_GRACE_MS);
close(fd);
fd = 0;
int exited = 0;
for (int i = 0; i < SHUTDOWN_POLL_MAX; i++) {
if (waitpid(child_pid, NULL, WNOHANG) != 0) {
exited = 1;
break;
}
sleep_ms(SHUTDOWN_POLL_MS);
}
if (!exited) {
kill(child_pid, SIGTERM);
for (int i = 0; i < SIGTERM_POLL_MAX; i++) {
if (waitpid(child_pid, NULL, WNOHANG) != 0) { exited = 1; break; }
sleep_ms(SHUTDOWN_POLL_MS);
}
if (!exited) {
kill(child_pid, SIGKILL);
waitpid(child_pid, NULL, 0);
}
}
child_pid = 0;
}
/* Reset output buffer for clean restart */
output_len = 0;
output_buffer[0] = '\0';
/* Brief delay to let stabber release old connection state */
usleep(200000); /* 200ms */
}
void

View File

@@ -11,17 +11,17 @@ extern char xdg_data_home[256];
extern int stub_port;
int init_prof_test(void **state);
int close_prof_test(void **state);
int init_prof_test(void** state);
int close_prof_test(void** state);
void prof_start(void);
void prof_stop(void);
void prof_connect(void);
void prof_connect_with_roster(const char *roster);
void prof_input(const char *input);
void prof_connect_with_roster(const char* roster);
void prof_input(const char* input);
int prof_output_exact(const char *text);
int prof_output_regex(const char *text);
int prof_output_exact(const char* text);
int prof_output_regex(const char* text);
void prof_timeout(int timeout);
void prof_timeout_reset(void);

View File

@@ -36,7 +36,7 @@
* it was persisted to and read back from the database
*/
void
message_db_history_on_reopen(void **state)
message_db_history_on_reopen(void** state)
{
prof_connect();
@@ -81,7 +81,7 @@ message_db_history_on_reopen(void **state)
* we can reliably synchronise on each delivery before proceeding.
*/
void
message_db_history_multiple(void **state)
message_db_history_multiple(void** state)
{
prof_connect();
@@ -131,7 +131,7 @@ message_db_history_multiple(void **state)
* 3. Open buddy1 → verify buddy1's body present, buddy2's body absent
*/
void
message_db_history_contact_isolation(void **state)
message_db_history_contact_isolation(void** state)
{
prof_connect();
@@ -177,7 +177,7 @@ message_db_history_contact_isolation(void **state)
* characters and return them unchanged.
*/
void
message_db_history_special_chars(void **state)
message_db_history_special_chars(void** state)
{
prof_connect();
@@ -210,7 +210,7 @@ message_db_history_special_chars(void **state)
* and catches crashes in the outgoing DB code.
*/
void
message_db_history_outgoing(void **state)
message_db_history_outgoing(void** state)
{
prof_connect();
@@ -238,7 +238,7 @@ message_db_history_outgoing(void **state)
* and the outgoing write path completed without error.
*/
void
message_db_history_dialog(void **state)
message_db_history_dialog(void** state)
{
prof_connect();
@@ -279,7 +279,7 @@ message_db_history_dialog(void **state)
* does not crash and the window is usable.
*/
void
message_db_history_empty(void **state)
message_db_history_empty(void** state)
{
prof_connect();
@@ -299,7 +299,7 @@ message_db_history_empty(void **state)
* Test: a long message (1000+ characters) is not truncated by the DB.
*/
void
message_db_history_long_message(void **state)
message_db_history_long_message(void** state)
{
prof_connect();
@@ -313,10 +313,11 @@ message_db_history_long_message(void **state)
char xml[1500];
snprintf(xml, sizeof(xml),
"<message id='long1' to='stabber@localhost' "
"from='buddy1@localhost/phone' type='chat'>"
"<body>%s</body>"
"</message>", body);
"<message id='long1' to='stabber@localhost' "
"from='buddy1@localhost/phone' type='chat'>"
"<body>%s</body>"
"</message>",
body);
stbbr_send(xml);
assert_true(prof_output_exact("<< chat message: Buddy1/phone (win 2)"));
@@ -335,7 +336,7 @@ message_db_history_long_message(void **state)
* Uses XML character reference &#10; for literal newline in body.
*/
void
message_db_history_newline(void **state)
message_db_history_newline(void** state)
{
prof_connect();
@@ -364,7 +365,7 @@ message_db_history_newline(void **state)
* percent, braces, brackets, equals) survive DB round-trip.
*/
void
message_db_history_service_chars(void **state)
message_db_history_service_chars(void** state)
{
prof_connect();
@@ -389,7 +390,7 @@ message_db_history_service_chars(void **state)
* Test: /history verify reports no issues after normal message writes.
*/
void
message_db_history_verify(void **state)
message_db_history_verify(void** state)
{
prof_connect();
@@ -425,7 +426,7 @@ message_db_history_verify(void **state)
* text should appear — the original should be absent.
*/
void
message_db_history_lmc(void **state)
message_db_history_lmc(void** state)
{
prof_connect();

View File

@@ -1,12 +1,12 @@
void message_db_history_on_reopen(void **state);
void message_db_history_multiple(void **state);
void message_db_history_contact_isolation(void **state);
void message_db_history_special_chars(void **state);
void message_db_history_outgoing(void **state);
void message_db_history_dialog(void **state);
void message_db_history_empty(void **state);
void message_db_history_long_message(void **state);
void message_db_history_newline(void **state);
void message_db_history_service_chars(void **state);
void message_db_history_verify(void **state);
void message_db_history_lmc(void **state);
void message_db_history_on_reopen(void** state);
void message_db_history_multiple(void** state);
void message_db_history_contact_isolation(void** state);
void message_db_history_special_chars(void** state);
void message_db_history_outgoing(void** state);
void message_db_history_dialog(void** state);
void message_db_history_empty(void** state);
void message_db_history_long_message(void** state);
void message_db_history_newline(void** state);
void message_db_history_service_chars(void** state);
void message_db_history_verify(void** state);
void message_db_history_lmc(void** state);