From 483638a7e6de31fa0d4e90446db7797a6e4099dd Mon Sep 17 00:00:00 2001 From: Cory McWilliams Date: Sat, 12 Feb 2022 01:44:11 +0000 Subject: [PATCH] I guess we support sub-millisecond timestamps. Who knew? git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@3835 ed5197a5-7fde-0310-b194-c3ffbd925b24 --- src/ssb.db.c | 106 +++++++++++++++++++++++++++++++++++---------------- src/ssb.db.h | 2 +- src/ssb.js.c | 4 +- 3 files changed, 76 insertions(+), 36 deletions(-) diff --git a/src/ssb.db.c b/src/ssb.db.c index d77aa387..a90bbe7a 100644 --- a/src/ssb.db.c +++ b/src/ssb.db.c @@ -9,49 +9,56 @@ #include #include +static void _tf_ssb_db_exec(sqlite3* db, const char* statement) +{ + char* error = NULL; + int result = sqlite3_exec(db, statement, NULL, NULL, &error); + if (result != SQLITE_OK) + { + printf("Error running '%s': %s.\n", statement, error); + abort(); + } +} + void tf_ssb_db_init(tf_ssb_t* ssb) { sqlite3* db = tf_ssb_get_db(ssb); - sqlite3_exec(db, "PRAGMA journal_mode = WAL", NULL, NULL, NULL); - sqlite3_exec(db, "PRAGMA synchronous = NORMAL", NULL, NULL, NULL); - sqlite3_exec(db, + _tf_ssb_db_exec(db, "PRAGMA journal_mode = WAL"); + _tf_ssb_db_exec(db, "PRAGMA synchronous = NORMAL"); + _tf_ssb_db_exec(db, "CREATE TABLE IF NOT EXISTS messages (" " author TEXT," " id TEXT PRIMARY KEY," " sequence INTEGER," - " timestamp INTEGER," + " timestamp REAL," " previous TEXT," " hash TEXT," " content TEXT," " signature TEXT," + " sequence_before_author INTEGER," " UNIQUE(author, sequence)" - ")", - NULL, NULL, NULL); - sqlite3_exec(db, "ALTER TABLE messages ADD COLUMN sequence_before_author INTEGER", NULL, NULL, NULL); - sqlite3_exec(db, "CREATE INDEX IF NOT EXISTS messages_author_id_index ON messages (author, id)", NULL, NULL, NULL); - sqlite3_exec(db, "CREATE INDEX IF NOT EXISTS messages_author_sequence_index ON messages (author, sequence)", NULL, NULL, NULL); - sqlite3_exec(db, "CREATE INDEX IF NOT EXISTS messages_author_timestamp_index ON messages (author, timestamp)", NULL, NULL, NULL); - sqlite3_exec(db, + ")"); + _tf_ssb_db_exec(db, "CREATE INDEX IF NOT EXISTS messages_author_id_index ON messages (author, id)"); + _tf_ssb_db_exec(db, "CREATE INDEX IF NOT EXISTS messages_author_sequence_index ON messages (author, sequence)"); + _tf_ssb_db_exec(db, "CREATE INDEX IF NOT EXISTS messages_author_timestamp_index ON messages (author, timestamp)"); + _tf_ssb_db_exec(db, "CREATE TABLE IF NOT EXISTS blobs (" " id TEXT PRIMARY KEY," " content BLOB," " created INTEGER" - ")", - NULL, NULL, NULL); - sqlite3_exec(db, + ")"); + _tf_ssb_db_exec(db, "CREATE TABLE IF NOT EXISTS blob_wants (" " id TEXT PRIMARY KEY" - ")", - NULL, NULL, NULL); - sqlite3_exec(db, + ")"); + _tf_ssb_db_exec(db, "CREATE TABLE IF NOT EXISTS properties (" " id TEXT," " key TEXT," " value TEXT," " UNIQUE(id, key)" - ")", - NULL, NULL, NULL); - sqlite3_exec(db, + ")"); + _tf_ssb_db_exec(db, "CREATE TABLE IF NOT EXISTS connections (" " host TEXT," " port INTEGER," @@ -59,8 +66,41 @@ void tf_ssb_db_init(tf_ssb_t* ssb) " last_attempt INTEGER," " last_success INTEGER," " UNIQUE(host, port, key)" - ")", - NULL, NULL, NULL); + ")"); + + bool need_add_sequence_before_author = true; + bool need_convert_timestamp_to_real = false; + + sqlite3_stmt* statement = NULL; + if (sqlite3_prepare(db, "PRAGMA table_info(messages)", -1, &statement, NULL) == SQLITE_OK) + { + while (sqlite3_step(statement) == SQLITE_ROW) + { + const char* name = (const char*)sqlite3_column_text(statement, 1); + const char* type = (const char*)sqlite3_column_text(statement, 1); + if (name && type && strcmp(name, "timestamp") == 0 && strcmp(type, "INTEGER") == 0) + { + need_convert_timestamp_to_real = true; + } + if (name && strcmp(name, "sequence_before_author") == 0) + { + need_add_sequence_before_author = false; + } + } + sqlite3_finalize(statement); + } + + if (need_convert_timestamp_to_real) + { + _tf_ssb_db_exec(db, "ALTER TABLE messages ADD COLUMN timestamp_real REAL"); + _tf_ssb_db_exec(db, "UPDATE messages SET timestamp_real = timestamp"); + _tf_ssb_db_exec(db, "ALTER TABLE messages DROP COLUMN timestamp REAL"); + _tf_ssb_db_exec(db, "ALTER TABLE messages RENAME COLUMN timestamp_real TO timestamp"); + } + if (need_add_sequence_before_author) + { + _tf_ssb_db_exec(db, "ALTER TABLE messages ADD COLUMN sequence_before_author INTEGER"); + } } static bool _tf_ssb_db_previous_message_exists(sqlite3* db, const char* author, int64_t sequence, const char* previous) @@ -97,8 +137,8 @@ bool tf_ssb_db_store_message(tf_ssb_t* ssb, JSContext* context, const char* id, const char* author = JS_ToCString(context, authorval); int64_t sequence = -1; JS_ToInt64(context, &sequence, JS_GetPropertyStr(context, val, "sequence")); - int64_t timestamp = -1; - JS_ToInt64(context, ×tamp, JS_GetPropertyStr(context, val, "timestamp")); + double timestamp = -1.0; + JS_ToFloat64(context, ×tamp, JS_GetPropertyStr(context, val, "timestamp")); JSValue contentval = JS_GetPropertyStr(context, val, "content"); JSValue content = JS_JSONStringify(context, contentval, JS_NULL, JS_NULL); @@ -119,7 +159,7 @@ bool tf_ssb_db_store_message(tf_ssb_t* ssb, JSContext* context, const char* id, (previous ? sqlite3_bind_text(statement, 2, previous, -1, NULL) : sqlite3_bind_null(statement, 2)) == SQLITE_OK && sqlite3_bind_text(statement, 3, author, -1, NULL) == SQLITE_OK && sqlite3_bind_int64(statement, 4, sequence) == SQLITE_OK && - sqlite3_bind_int64(statement, 5, timestamp) == SQLITE_OK && + sqlite3_bind_double(statement, 5, timestamp) == SQLITE_OK && sqlite3_bind_text(statement, 6, contentstr, content_len, NULL) == SQLITE_OK && sqlite3_bind_text(statement, 7, "sha256", 6, NULL) == SQLITE_OK && sqlite3_bind_text(statement, 8, signature, -1, NULL) == SQLITE_OK && @@ -304,7 +344,7 @@ bool tf_ssb_db_blob_store(tf_ssb_t* ssb, const uint8_t* blob, size_t size, char* return result; } -bool tf_ssb_db_get_message_by_author_and_sequence(tf_ssb_t* ssb, const char* author, int64_t sequence, char* out_message_id, size_t out_message_id_size, int64_t* out_timestamp, char** out_content) +bool tf_ssb_db_get_message_by_author_and_sequence(tf_ssb_t* ssb, const char* author, int64_t sequence, char* out_message_id, size_t out_message_id_size, double* out_timestamp, char** out_content) { bool found = false; sqlite3_stmt* statement; @@ -321,7 +361,7 @@ bool tf_ssb_db_get_message_by_author_and_sequence(tf_ssb_t* ssb, const char* aut } if (out_timestamp) { - *out_timestamp = sqlite3_column_int64(statement, 1); + *out_timestamp = sqlite3_column_double(statement, 1); } if (out_content) { @@ -508,7 +548,7 @@ void tf_ssb_db_visit_query(tf_ssb_t* ssb, const char* query, const JSValue binds sqlite3_set_authorizer(db, NULL, NULL); } -static JSValue _tf_ssb_format_message(JSContext* context, const char* previous, const char* author, int64_t sequence, int64_t timestamp, const char* hash, const char* content, const char* signature, bool sequence_before_author) +static JSValue _tf_ssb_format_message(JSContext* context, const char* previous, const char* author, int64_t sequence, double timestamp, const char* hash, const char* content, const char* signature, bool sequence_before_author) { JSValue value = JS_NewObject(context); JS_SetPropertyStr(context, value, "previous", previous ? JS_NewString(context, previous) : JS_NULL); @@ -522,7 +562,7 @@ static JSValue _tf_ssb_format_message(JSContext* context, const char* previous, JS_SetPropertyStr(context, value, "author", JS_NewString(context, author)); JS_SetPropertyStr(context, value, "sequence", JS_NewInt64(context, sequence)); } - JS_SetPropertyStr(context, value, "timestamp", JS_NewInt64(context, timestamp)); + JS_SetPropertyStr(context, value, "timestamp", JS_NewFloat64(context, timestamp)); JS_SetPropertyStr(context, value, "hash", JS_NewString(context, hash)); JS_SetPropertyStr(context, value, "content", JS_ParseJSON(context, content, strlen(content), NULL)); JS_SetPropertyStr(context, value, "signature", JS_NewString(context, signature)); @@ -567,7 +607,7 @@ bool tf_ssb_db_check(sqlite3* db, const char* check_author) const char* previous = (const char*)sqlite3_column_text(statement, 1); const char* author = (const char*)sqlite3_column_text(statement, 2); int64_t sequence = sqlite3_column_int64(statement, 3); - int64_t timestamp = sqlite3_column_int64(statement, 4); + double timestamp = sqlite3_column_double(statement, 4); const char* hash = (const char*)sqlite3_column_text(statement, 5); const char* content = (const char*)sqlite3_column_text(statement, 6); const char* signature = (const char*)sqlite3_column_text(statement, 7); @@ -580,10 +620,10 @@ bool tf_ssb_db_check(sqlite3* db, const char* check_author) const char* jv = JS_ToCString(context, j); if (tf_ssb_verify_and_strip_signature(context, message, actual_id, sizeof(actual_id), out_signature, sizeof(out_signature), &actual_sequence_before_author)) { - /*if (previous && strcmp(previous, previous_id)) + if (previous && strcmp(previous, previous_id)) { printf("%s:%d previous was %s should be %s\n", id, (int)sequence, previous_id, previous); - }*/ + } if (strcmp(id, actual_id)) { if (_tf_ssb_update_message_id(db, id, actual_id)) @@ -598,7 +638,7 @@ bool tf_ssb_db_check(sqlite3* db, const char* check_author) } else { - printf("unable to verify signature for %s\n", id); + printf("%s sequence=%" PRId64 " unable to verify signature for %s sequence_before_author=%d message=[%.*s]\n", author, sequence, id, sequence_before_author, (int)strlen(jv), jv); } JS_FreeCString(context, jv); JS_FreeValue(context, j); diff --git a/src/ssb.db.h b/src/ssb.db.h index 20f8c6ed..3016eea4 100644 --- a/src/ssb.db.h +++ b/src/ssb.db.h @@ -11,7 +11,7 @@ bool tf_ssb_db_message_content_get(tf_ssb_t* ssb, const char* id, uint8_t** out_ bool tf_ssb_db_blob_get(tf_ssb_t* ssb, const char* id, uint8_t** out_blob, size_t* out_size); bool tf_ssb_db_blob_store(tf_ssb_t* ssb, const uint8_t* blob, size_t size, char* out_id, size_t out_id_size); -bool tf_ssb_db_get_message_by_author_and_sequence(tf_ssb_t* ssb, const char* author, int64_t sequence, char* out_message_id, size_t out_message_id_size, int64_t* out_timestamp, char** out_content); +bool tf_ssb_db_get_message_by_author_and_sequence(tf_ssb_t* ssb, const char* author, int64_t sequence, char* out_message_id, size_t out_message_id_size, double* out_timestamp, char** out_content); bool tf_ssb_db_get_latest_message_by_author(tf_ssb_t* ssb, const char* author, int64_t* out_sequence, char* out_message_id, size_t out_message_id_size); void tf_ssb_db_visit_query(tf_ssb_t* ssb, const char* query, const JSValue binds, void (*callback)(JSValue row, void* user_data), void* user_data); diff --git a/src/ssb.js.c b/src/ssb.js.c index d6f40f69..293760e1 100644 --- a/src/ssb.js.c +++ b/src/ssb.js.c @@ -41,12 +41,12 @@ static JSValue _tf_ssb_getMessage(JSContext* context, JSValueConst this_val, int const char* id = JS_ToCString(context, argv[0]); int64_t sequence = 0; JS_ToInt64(context, &sequence, argv[1]); - int64_t timestamp = -1; + double timestamp = -1.0; char* contents = NULL; if (tf_ssb_db_get_message_by_author_and_sequence(ssb, id, sequence, NULL, 0, ×tamp, &contents)) { result = JS_NewObject(context); - JS_SetPropertyStr(context, result, "timestamp", JS_NewInt64(context, timestamp)); + JS_SetPropertyStr(context, result, "timestamp", JS_NewFloat64(context, timestamp)); JS_SetPropertyStr(context, result, "content", JS_NewString(context, contents)); free(contents); }