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
This commit is contained in:
parent
50bef73200
commit
483638a7e6
106
src/ssb.db.c
106
src/ssb.db.c
@ -9,49 +9,56 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
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)
|
void tf_ssb_db_init(tf_ssb_t* ssb)
|
||||||
{
|
{
|
||||||
sqlite3* db = tf_ssb_get_db(ssb);
|
sqlite3* db = tf_ssb_get_db(ssb);
|
||||||
sqlite3_exec(db, "PRAGMA journal_mode = WAL", NULL, NULL, NULL);
|
_tf_ssb_db_exec(db, "PRAGMA journal_mode = WAL");
|
||||||
sqlite3_exec(db, "PRAGMA synchronous = NORMAL", NULL, NULL, NULL);
|
_tf_ssb_db_exec(db, "PRAGMA synchronous = NORMAL");
|
||||||
sqlite3_exec(db,
|
_tf_ssb_db_exec(db,
|
||||||
"CREATE TABLE IF NOT EXISTS messages ("
|
"CREATE TABLE IF NOT EXISTS messages ("
|
||||||
" author TEXT,"
|
" author TEXT,"
|
||||||
" id TEXT PRIMARY KEY,"
|
" id TEXT PRIMARY KEY,"
|
||||||
" sequence INTEGER,"
|
" sequence INTEGER,"
|
||||||
" timestamp INTEGER,"
|
" timestamp REAL,"
|
||||||
" previous TEXT,"
|
" previous TEXT,"
|
||||||
" hash TEXT,"
|
" hash TEXT,"
|
||||||
" content TEXT,"
|
" content TEXT,"
|
||||||
" signature TEXT,"
|
" signature TEXT,"
|
||||||
|
" sequence_before_author INTEGER,"
|
||||||
" UNIQUE(author, sequence)"
|
" UNIQUE(author, sequence)"
|
||||||
")",
|
")");
|
||||||
NULL, NULL, NULL);
|
_tf_ssb_db_exec(db, "CREATE INDEX IF NOT EXISTS messages_author_id_index ON messages (author, id)");
|
||||||
sqlite3_exec(db, "ALTER TABLE messages ADD COLUMN sequence_before_author INTEGER", NULL, NULL, NULL);
|
_tf_ssb_db_exec(db, "CREATE INDEX IF NOT EXISTS messages_author_sequence_index ON messages (author, sequence)");
|
||||||
sqlite3_exec(db, "CREATE INDEX IF NOT EXISTS messages_author_id_index ON messages (author, id)", NULL, NULL, NULL);
|
_tf_ssb_db_exec(db, "CREATE INDEX IF NOT EXISTS messages_author_timestamp_index ON messages (author, timestamp)");
|
||||||
sqlite3_exec(db, "CREATE INDEX IF NOT EXISTS messages_author_sequence_index ON messages (author, sequence)", NULL, NULL, NULL);
|
_tf_ssb_db_exec(db,
|
||||||
sqlite3_exec(db, "CREATE INDEX IF NOT EXISTS messages_author_timestamp_index ON messages (author, timestamp)", NULL, NULL, NULL);
|
|
||||||
sqlite3_exec(db,
|
|
||||||
"CREATE TABLE IF NOT EXISTS blobs ("
|
"CREATE TABLE IF NOT EXISTS blobs ("
|
||||||
" id TEXT PRIMARY KEY,"
|
" id TEXT PRIMARY KEY,"
|
||||||
" content BLOB,"
|
" content BLOB,"
|
||||||
" created INTEGER"
|
" created INTEGER"
|
||||||
")",
|
")");
|
||||||
NULL, NULL, NULL);
|
_tf_ssb_db_exec(db,
|
||||||
sqlite3_exec(db,
|
|
||||||
"CREATE TABLE IF NOT EXISTS blob_wants ("
|
"CREATE TABLE IF NOT EXISTS blob_wants ("
|
||||||
" id TEXT PRIMARY KEY"
|
" id TEXT PRIMARY KEY"
|
||||||
")",
|
")");
|
||||||
NULL, NULL, NULL);
|
_tf_ssb_db_exec(db,
|
||||||
sqlite3_exec(db,
|
|
||||||
"CREATE TABLE IF NOT EXISTS properties ("
|
"CREATE TABLE IF NOT EXISTS properties ("
|
||||||
" id TEXT,"
|
" id TEXT,"
|
||||||
" key TEXT,"
|
" key TEXT,"
|
||||||
" value TEXT,"
|
" value TEXT,"
|
||||||
" UNIQUE(id, key)"
|
" UNIQUE(id, key)"
|
||||||
")",
|
")");
|
||||||
NULL, NULL, NULL);
|
_tf_ssb_db_exec(db,
|
||||||
sqlite3_exec(db,
|
|
||||||
"CREATE TABLE IF NOT EXISTS connections ("
|
"CREATE TABLE IF NOT EXISTS connections ("
|
||||||
" host TEXT,"
|
" host TEXT,"
|
||||||
" port INTEGER,"
|
" port INTEGER,"
|
||||||
@ -59,8 +66,41 @@ void tf_ssb_db_init(tf_ssb_t* ssb)
|
|||||||
" last_attempt INTEGER,"
|
" last_attempt INTEGER,"
|
||||||
" last_success INTEGER,"
|
" last_success INTEGER,"
|
||||||
" UNIQUE(host, port, key)"
|
" 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)
|
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);
|
const char* author = JS_ToCString(context, authorval);
|
||||||
int64_t sequence = -1;
|
int64_t sequence = -1;
|
||||||
JS_ToInt64(context, &sequence, JS_GetPropertyStr(context, val, "sequence"));
|
JS_ToInt64(context, &sequence, JS_GetPropertyStr(context, val, "sequence"));
|
||||||
int64_t timestamp = -1;
|
double timestamp = -1.0;
|
||||||
JS_ToInt64(context, ×tamp, JS_GetPropertyStr(context, val, "timestamp"));
|
JS_ToFloat64(context, ×tamp, JS_GetPropertyStr(context, val, "timestamp"));
|
||||||
|
|
||||||
JSValue contentval = JS_GetPropertyStr(context, val, "content");
|
JSValue contentval = JS_GetPropertyStr(context, val, "content");
|
||||||
JSValue content = JS_JSONStringify(context, contentval, JS_NULL, JS_NULL);
|
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 &&
|
(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_text(statement, 3, author, -1, NULL) == SQLITE_OK &&
|
||||||
sqlite3_bind_int64(statement, 4, sequence) == 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, 6, contentstr, content_len, NULL) == SQLITE_OK &&
|
||||||
sqlite3_bind_text(statement, 7, "sha256", 6, NULL) == SQLITE_OK &&
|
sqlite3_bind_text(statement, 7, "sha256", 6, NULL) == SQLITE_OK &&
|
||||||
sqlite3_bind_text(statement, 8, signature, -1, 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;
|
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;
|
bool found = false;
|
||||||
sqlite3_stmt* statement;
|
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)
|
if (out_timestamp)
|
||||||
{
|
{
|
||||||
*out_timestamp = sqlite3_column_int64(statement, 1);
|
*out_timestamp = sqlite3_column_double(statement, 1);
|
||||||
}
|
}
|
||||||
if (out_content)
|
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);
|
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);
|
JSValue value = JS_NewObject(context);
|
||||||
JS_SetPropertyStr(context, value, "previous", previous ? JS_NewString(context, previous) : JS_NULL);
|
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, "author", JS_NewString(context, author));
|
||||||
JS_SetPropertyStr(context, value, "sequence", JS_NewInt64(context, sequence));
|
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, "hash", JS_NewString(context, hash));
|
||||||
JS_SetPropertyStr(context, value, "content", JS_ParseJSON(context, content, strlen(content), NULL));
|
JS_SetPropertyStr(context, value, "content", JS_ParseJSON(context, content, strlen(content), NULL));
|
||||||
JS_SetPropertyStr(context, value, "signature", JS_NewString(context, signature));
|
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* previous = (const char*)sqlite3_column_text(statement, 1);
|
||||||
const char* author = (const char*)sqlite3_column_text(statement, 2);
|
const char* author = (const char*)sqlite3_column_text(statement, 2);
|
||||||
int64_t sequence = sqlite3_column_int64(statement, 3);
|
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* hash = (const char*)sqlite3_column_text(statement, 5);
|
||||||
const char* content = (const char*)sqlite3_column_text(statement, 6);
|
const char* content = (const char*)sqlite3_column_text(statement, 6);
|
||||||
const char* signature = (const char*)sqlite3_column_text(statement, 7);
|
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);
|
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 (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);
|
printf("%s:%d previous was %s should be %s\n", id, (int)sequence, previous_id, previous);
|
||||||
}*/
|
}
|
||||||
if (strcmp(id, actual_id))
|
if (strcmp(id, actual_id))
|
||||||
{
|
{
|
||||||
if (_tf_ssb_update_message_id(db, 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
|
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_FreeCString(context, jv);
|
||||||
JS_FreeValue(context, j);
|
JS_FreeValue(context, j);
|
||||||
|
@ -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_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_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);
|
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);
|
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);
|
||||||
|
|
||||||
|
@ -41,12 +41,12 @@ static JSValue _tf_ssb_getMessage(JSContext* context, JSValueConst this_val, int
|
|||||||
const char* id = JS_ToCString(context, argv[0]);
|
const char* id = JS_ToCString(context, argv[0]);
|
||||||
int64_t sequence = 0;
|
int64_t sequence = 0;
|
||||||
JS_ToInt64(context, &sequence, argv[1]);
|
JS_ToInt64(context, &sequence, argv[1]);
|
||||||
int64_t timestamp = -1;
|
double timestamp = -1.0;
|
||||||
char* contents = NULL;
|
char* contents = NULL;
|
||||||
if (tf_ssb_db_get_message_by_author_and_sequence(ssb, id, sequence, NULL, 0, ×tamp, &contents))
|
if (tf_ssb_db_get_message_by_author_and_sequence(ssb, id, sequence, NULL, 0, ×tamp, &contents))
|
||||||
{
|
{
|
||||||
result = JS_NewObject(context);
|
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));
|
JS_SetPropertyStr(context, result, "content", JS_NewString(context, contents));
|
||||||
free(contents);
|
free(contents);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user