Allow the DB writer to be used from a worker thread. Not well tested, just still trying to charge forward on moving all blocking work off the main thread.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4325 ed5197a5-7fde-0310-b194-c3ffbd925b24
This commit is contained in:
		@@ -19,7 +19,6 @@ typedef struct _database_t
 | 
				
			|||||||
	JSContext* context;
 | 
						JSContext* context;
 | 
				
			||||||
	JSValue object;
 | 
						JSValue object;
 | 
				
			||||||
	void* task;
 | 
						void* task;
 | 
				
			||||||
	sqlite3* db;
 | 
					 | 
				
			||||||
	const char* id;
 | 
						const char* id;
 | 
				
			||||||
} database_t;
 | 
					} database_t;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -62,16 +61,12 @@ static JSValue _database_create(JSContext* context, JSValueConst this_val, int a
 | 
				
			|||||||
	++_database_count;
 | 
						++_database_count;
 | 
				
			||||||
	JSValue object = JS_NewObjectClass(context, _database_class_id);
 | 
						JSValue object = JS_NewObjectClass(context, _database_class_id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	tf_task_t* task = tf_task_get(context);
 | 
					 | 
				
			||||||
	sqlite3* db = tf_ssb_get_db(tf_task_get_ssb(task));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	database_t* database = tf_malloc(sizeof(database_t));
 | 
						database_t* database = tf_malloc(sizeof(database_t));
 | 
				
			||||||
	*database = (database_t)
 | 
						*database = (database_t)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		.task = JS_GetContextOpaque(context),
 | 
							.task = JS_GetContextOpaque(context),
 | 
				
			||||||
		.context = context,
 | 
							.context = context,
 | 
				
			||||||
		.object = object,
 | 
							.object = object,
 | 
				
			||||||
		.db = db,
 | 
					 | 
				
			||||||
	};
 | 
						};
 | 
				
			||||||
	const char* id = JS_ToCString(context, argv[0]);
 | 
						const char* id = JS_ToCString(context, argv[0]);
 | 
				
			||||||
	database->id = tf_strdup(id);
 | 
						database->id = tf_strdup(id);
 | 
				
			||||||
@@ -105,8 +100,10 @@ static JSValue _database_get(JSContext* context, JSValueConst this_val, int argc
 | 
				
			|||||||
	database_t* database = JS_GetOpaque(this_val, _database_class_id);
 | 
						database_t* database = JS_GetOpaque(this_val, _database_class_id);
 | 
				
			||||||
	if (database)
 | 
						if (database)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
 | 
							tf_ssb_t* ssb = tf_task_get_ssb(database->task);
 | 
				
			||||||
		sqlite3_stmt* statement;
 | 
							sqlite3_stmt* statement;
 | 
				
			||||||
		if (sqlite3_prepare(database->db, "SELECT value FROM properties WHERE id = ? AND key = ?", -1, &statement, NULL) == SQLITE_OK)
 | 
							sqlite3* db = tf_ssb_acquire_db_reader(ssb);
 | 
				
			||||||
 | 
							if (sqlite3_prepare(db, "SELECT value FROM properties WHERE id = ? AND key = ?", -1, &statement, NULL) == SQLITE_OK)
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			size_t length;
 | 
								size_t length;
 | 
				
			||||||
			const char* keyString = JS_ToCStringLen(context, &length, argv[0]);
 | 
								const char* keyString = JS_ToCStringLen(context, &length, argv[0]);
 | 
				
			||||||
@@ -119,6 +116,7 @@ static JSValue _database_get(JSContext* context, JSValueConst this_val, int argc
 | 
				
			|||||||
			JS_FreeCString(context, keyString);
 | 
								JS_FreeCString(context, keyString);
 | 
				
			||||||
			sqlite3_finalize(statement);
 | 
								sqlite3_finalize(statement);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
							tf_ssb_release_db_reader(ssb, db);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return entry;
 | 
						return entry;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -129,7 +127,9 @@ JSValue _database_set(JSContext* context, JSValueConst this_val, int argc, JSVal
 | 
				
			|||||||
	if (database)
 | 
						if (database)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		sqlite3_stmt* statement;
 | 
							sqlite3_stmt* statement;
 | 
				
			||||||
		if (sqlite3_prepare(database->db, "INSERT OR REPLACE INTO properties (id, key, value) VALUES ($1, $2, $3)", -1, &statement, NULL) == SQLITE_OK)
 | 
							tf_ssb_t* ssb = tf_task_get_ssb(database->task);
 | 
				
			||||||
 | 
							sqlite3* db = tf_ssb_acquire_db_writer(ssb);
 | 
				
			||||||
 | 
							if (sqlite3_prepare(db, "INSERT OR REPLACE INTO properties (id, key, value) VALUES ($1, $2, $3)", -1, &statement, NULL) == SQLITE_OK)
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			size_t keyLength;
 | 
								size_t keyLength;
 | 
				
			||||||
			const char* keyString = JS_ToCStringLen(context, &keyLength, argv[0]);
 | 
								const char* keyString = JS_ToCStringLen(context, &keyLength, argv[0]);
 | 
				
			||||||
@@ -145,6 +145,7 @@ JSValue _database_set(JSContext* context, JSValueConst this_val, int argc, JSVal
 | 
				
			|||||||
			JS_FreeCString(context, valueString);
 | 
								JS_FreeCString(context, valueString);
 | 
				
			||||||
			sqlite3_finalize(statement);
 | 
								sqlite3_finalize(statement);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
							tf_ssb_release_db_writer(ssb, db);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return JS_UNDEFINED;
 | 
						return JS_UNDEFINED;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -156,9 +157,11 @@ static JSValue _database_exchange(JSContext* context, JSValueConst this_val, int
 | 
				
			|||||||
	if (database)
 | 
						if (database)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		sqlite3_stmt* statement;
 | 
							sqlite3_stmt* statement;
 | 
				
			||||||
 | 
							tf_ssb_t* ssb = tf_task_get_ssb(database->task);
 | 
				
			||||||
 | 
							sqlite3* db = tf_ssb_acquire_db_writer(ssb);
 | 
				
			||||||
		if (JS_IsNull(argv[1]) || JS_IsUndefined(argv[1]))
 | 
							if (JS_IsNull(argv[1]) || JS_IsUndefined(argv[1]))
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			if (sqlite3_prepare(database->db, "INSERT INTO properties (id, key, value) VALUES ($1, $2, $3) ON CONFLICT DO NOTHING", -1, &statement, NULL) == SQLITE_OK)
 | 
								if (sqlite3_prepare(db, "INSERT INTO properties (id, key, value) VALUES ($1, $2, $3) ON CONFLICT DO NOTHING", -1, &statement, NULL) == SQLITE_OK)
 | 
				
			||||||
			{
 | 
								{
 | 
				
			||||||
				size_t key_length;
 | 
									size_t key_length;
 | 
				
			||||||
				size_t set_length;
 | 
									size_t set_length;
 | 
				
			||||||
@@ -169,14 +172,14 @@ static JSValue _database_exchange(JSContext* context, JSValueConst this_val, int
 | 
				
			|||||||
					sqlite3_bind_text(statement, 3, set, set_length, NULL) == SQLITE_OK &&
 | 
										sqlite3_bind_text(statement, 3, set, set_length, NULL) == SQLITE_OK &&
 | 
				
			||||||
					sqlite3_step(statement) == SQLITE_DONE)
 | 
										sqlite3_step(statement) == SQLITE_DONE)
 | 
				
			||||||
				{
 | 
									{
 | 
				
			||||||
					exchanged = sqlite3_changes(database->db) != 0 ? JS_TRUE : JS_FALSE;
 | 
										exchanged = sqlite3_changes(db) != 0 ? JS_TRUE : JS_FALSE;
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
				JS_FreeCString(context, key);
 | 
									JS_FreeCString(context, key);
 | 
				
			||||||
				JS_FreeCString(context, set);
 | 
									JS_FreeCString(context, set);
 | 
				
			||||||
				sqlite3_finalize(statement);
 | 
									sqlite3_finalize(statement);
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		else if (sqlite3_prepare(database->db, "UPDATE properties SET value = $1 WHERE id = $2 AND key = $3 AND value = $4", -1, &statement, NULL) == SQLITE_OK)
 | 
							else if (sqlite3_prepare(db, "UPDATE properties SET value = $1 WHERE id = $2 AND key = $3 AND value = $4", -1, &statement, NULL) == SQLITE_OK)
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			size_t key_length;
 | 
								size_t key_length;
 | 
				
			||||||
			size_t expected_length;
 | 
								size_t expected_length;
 | 
				
			||||||
@@ -190,13 +193,14 @@ static JSValue _database_exchange(JSContext* context, JSValueConst this_val, int
 | 
				
			|||||||
				sqlite3_bind_text(statement, 4, expected, expected_length, NULL) == SQLITE_OK &&
 | 
									sqlite3_bind_text(statement, 4, expected, expected_length, NULL) == SQLITE_OK &&
 | 
				
			||||||
				sqlite3_step(statement) == SQLITE_DONE)
 | 
									sqlite3_step(statement) == SQLITE_DONE)
 | 
				
			||||||
			{
 | 
								{
 | 
				
			||||||
				exchanged = sqlite3_changes(database->db) != 0 ? JS_TRUE : JS_FALSE;
 | 
									exchanged = sqlite3_changes(db) != 0 ? JS_TRUE : JS_FALSE;
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			JS_FreeCString(context, key);
 | 
								JS_FreeCString(context, key);
 | 
				
			||||||
			JS_FreeCString(context, expected);
 | 
								JS_FreeCString(context, expected);
 | 
				
			||||||
			JS_FreeCString(context, set);
 | 
								JS_FreeCString(context, set);
 | 
				
			||||||
			sqlite3_finalize(statement);
 | 
								sqlite3_finalize(statement);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
							tf_ssb_release_db_writer(ssb, db);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return exchanged;
 | 
						return exchanged;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -207,7 +211,9 @@ JSValue _database_remove(JSContext* context, JSValueConst this_val, int argc, JS
 | 
				
			|||||||
	if (database)
 | 
						if (database)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		sqlite3_stmt* statement;
 | 
							sqlite3_stmt* statement;
 | 
				
			||||||
		if (sqlite3_prepare(database->db, "DELETE FROM properties WHERE id = $1 AND key = $2", -1, &statement, NULL) == SQLITE_OK)
 | 
							tf_ssb_t* ssb = tf_task_get_ssb(database->task);
 | 
				
			||||||
 | 
							sqlite3* db = tf_ssb_acquire_db_writer(ssb);
 | 
				
			||||||
 | 
							if (sqlite3_prepare(db, "DELETE FROM properties WHERE id = $1 AND key = $2", -1, &statement, NULL) == SQLITE_OK)
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			size_t keyLength;
 | 
								size_t keyLength;
 | 
				
			||||||
			const char* keyString = JS_ToCStringLen(context, &keyLength, argv[0]);
 | 
								const char* keyString = JS_ToCStringLen(context, &keyLength, argv[0]);
 | 
				
			||||||
@@ -219,6 +225,7 @@ JSValue _database_remove(JSContext* context, JSValueConst this_val, int argc, JS
 | 
				
			|||||||
			JS_FreeCString(context, keyString);
 | 
								JS_FreeCString(context, keyString);
 | 
				
			||||||
			sqlite3_finalize(statement);
 | 
								sqlite3_finalize(statement);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
							tf_ssb_release_db_writer(ssb, db);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return JS_UNDEFINED;
 | 
						return JS_UNDEFINED;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -230,7 +237,9 @@ JSValue _database_get_all(JSContext* context, JSValueConst this_val, int argc, J
 | 
				
			|||||||
	if (database)
 | 
						if (database)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		sqlite3_stmt* statement;
 | 
							sqlite3_stmt* statement;
 | 
				
			||||||
		if (sqlite3_prepare(database->db, "SELECT key, value FROM properties WHERE id = $1", -1, &statement, NULL) == SQLITE_OK)
 | 
							tf_ssb_t* ssb = tf_task_get_ssb(database->task);
 | 
				
			||||||
 | 
							sqlite3* db = tf_ssb_acquire_db_reader(ssb);
 | 
				
			||||||
 | 
							if (sqlite3_prepare(db, "SELECT key, value FROM properties WHERE id = $1", -1, &statement, NULL) == SQLITE_OK)
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			if (sqlite3_bind_text(statement, 1, database->id, -1, NULL) == SQLITE_OK)
 | 
								if (sqlite3_bind_text(statement, 1, database->id, -1, NULL) == SQLITE_OK)
 | 
				
			||||||
			{
 | 
								{
 | 
				
			||||||
@@ -243,6 +252,7 @@ JSValue _database_get_all(JSContext* context, JSValueConst this_val, int argc, J
 | 
				
			|||||||
			}
 | 
								}
 | 
				
			||||||
			sqlite3_finalize(statement);
 | 
								sqlite3_finalize(statement);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
							tf_ssb_release_db_reader(ssb, db);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return array;
 | 
						return array;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -254,7 +264,9 @@ JSValue _database_get_like(JSContext* context, JSValueConst this_val, int argc,
 | 
				
			|||||||
	if (database)
 | 
						if (database)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		sqlite3_stmt* statement;
 | 
							sqlite3_stmt* statement;
 | 
				
			||||||
		if (sqlite3_prepare(database->db, "SELECT key, value FROM properties WHERE id = ? AND KEY LIKE ?", -1, &statement, NULL) == SQLITE_OK)
 | 
							tf_ssb_t* ssb = tf_task_get_ssb(database->task);
 | 
				
			||||||
 | 
							sqlite3* db = tf_ssb_acquire_db_reader(ssb);
 | 
				
			||||||
 | 
							if (sqlite3_prepare(db, "SELECT key, value FROM properties WHERE id = ? AND KEY LIKE ?", -1, &statement, NULL) == SQLITE_OK)
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			const char* pattern = JS_ToCString(context, argv[0]);
 | 
								const char* pattern = JS_ToCString(context, argv[0]);
 | 
				
			||||||
			if (sqlite3_bind_text(statement, 1, database->id, -1, NULL) == SQLITE_OK &&
 | 
								if (sqlite3_bind_text(statement, 1, database->id, -1, NULL) == SQLITE_OK &&
 | 
				
			||||||
@@ -273,6 +285,7 @@ JSValue _database_get_like(JSContext* context, JSValueConst this_val, int argc,
 | 
				
			|||||||
			JS_FreeCString(context, pattern);
 | 
								JS_FreeCString(context, pattern);
 | 
				
			||||||
			sqlite3_finalize(statement);
 | 
								sqlite3_finalize(statement);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
							tf_ssb_release_db_reader(ssb, db);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return result;
 | 
						return result;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -280,8 +293,8 @@ JSValue _database_get_like(JSContext* context, JSValueConst this_val, int argc,
 | 
				
			|||||||
static JSValue _databases_list(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv, int magic, JSValue* data)
 | 
					static JSValue _databases_list(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv, int magic, JSValue* data)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	tf_task_t* task = tf_task_get(context);
 | 
						tf_task_t* task = tf_task_get(context);
 | 
				
			||||||
	sqlite3* db = tf_ssb_get_db(tf_task_get_ssb(task));
 | 
						tf_ssb_t* ssb = tf_task_get_ssb(task);
 | 
				
			||||||
 | 
						sqlite3* db = tf_ssb_acquire_db_reader(ssb);
 | 
				
			||||||
	JSValue array = JS_UNDEFINED;
 | 
						JSValue array = JS_UNDEFINED;
 | 
				
			||||||
	sqlite3_stmt* statement;
 | 
						sqlite3_stmt* statement;
 | 
				
			||||||
	if (sqlite3_prepare(db, "SELECT DISTINCT id FROM properties WHERE id LIKE ?", -1, &statement, NULL) == SQLITE_OK)
 | 
						if (sqlite3_prepare(db, "SELECT DISTINCT id FROM properties WHERE id LIKE ?", -1, &statement, NULL) == SQLITE_OK)
 | 
				
			||||||
@@ -299,5 +312,6 @@ static JSValue _databases_list(JSContext* context, JSValueConst this_val, int ar
 | 
				
			|||||||
		JS_FreeCString(context, pattern);
 | 
							JS_FreeCString(context, pattern);
 | 
				
			||||||
		sqlite3_finalize(statement);
 | 
							sqlite3_finalize(statement);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						tf_ssb_release_db_reader(ssb, db);
 | 
				
			||||||
	return array;
 | 
						return array;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										38
									
								
								src/ssb.c
									
									
									
									
									
								
							
							
						
						
									
										38
									
								
								src/ssb.c
									
									
									
									
									
								
							@@ -180,9 +180,10 @@ typedef struct _tf_ssb_t
 | 
				
			|||||||
	tf_trace_t* trace;
 | 
						tf_trace_t* trace;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const char* db_path;
 | 
						const char* db_path;
 | 
				
			||||||
	sqlite3* db;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	uv_mutex_t db_readers_lock;
 | 
						uv_mutex_t db_readers_lock;
 | 
				
			||||||
 | 
						uv_mutex_t db_writer_lock;
 | 
				
			||||||
 | 
						sqlite3* db_writer;
 | 
				
			||||||
	sqlite3** db_readers;
 | 
						sqlite3** db_readers;
 | 
				
			||||||
	int db_readers_count;
 | 
						int db_readers_count;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -2099,6 +2100,7 @@ tf_ssb_t* tf_ssb_create(uv_loop_t* loop, JSContext* context, const char* db_path
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	uv_mutex_init(&ssb->db_readers_lock);
 | 
						uv_mutex_init(&ssb->db_readers_lock);
 | 
				
			||||||
 | 
						uv_mutex_init(&ssb->db_writer_lock);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	JS_NewClassID(&_connection_class_id);
 | 
						JS_NewClassID(&_connection_class_id);
 | 
				
			||||||
	JSClassDef def =
 | 
						JSClassDef def =
 | 
				
			||||||
@@ -2109,7 +2111,7 @@ tf_ssb_t* tf_ssb_create(uv_loop_t* loop, JSContext* context, const char* db_path
 | 
				
			|||||||
	JS_NewClass(JS_GetRuntime(ssb->context), _connection_class_id, &def);
 | 
						JS_NewClass(JS_GetRuntime(ssb->context), _connection_class_id, &def);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ssb->db_path = tf_strdup(db_path);
 | 
						ssb->db_path = tf_strdup(db_path);
 | 
				
			||||||
	sqlite3_open(db_path, &ssb->db);
 | 
						sqlite3_open_v2(db_path, &ssb->db_writer, SQLITE_OPEN_READWRITE | SQLITE_OPEN_URI, NULL);
 | 
				
			||||||
	tf_ssb_db_init(ssb);
 | 
						tf_ssb_db_init(ssb);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (loop)
 | 
						if (loop)
 | 
				
			||||||
@@ -2142,11 +2144,6 @@ tf_ssb_t* tf_ssb_create(uv_loop_t* loop, JSContext* context, const char* db_path
 | 
				
			|||||||
	return ssb;
 | 
						return ssb;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
sqlite3* tf_ssb_get_db(tf_ssb_t* ssb)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	return ssb->db;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
sqlite3* tf_ssb_acquire_db_reader(tf_ssb_t* ssb)
 | 
					sqlite3* tf_ssb_acquire_db_reader(tf_ssb_t* ssb)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	sqlite3* db = NULL;
 | 
						sqlite3* db = NULL;
 | 
				
			||||||
@@ -2157,7 +2154,7 @@ sqlite3* tf_ssb_acquire_db_reader(tf_ssb_t* ssb)
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
	else
 | 
						else
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		sqlite3_open_v2(ssb->db_path, &db, SQLITE_OPEN_READONLY, NULL);
 | 
							sqlite3_open_v2(ssb->db_path, &db, SQLITE_OPEN_READONLY | SQLITE_OPEN_URI, NULL);
 | 
				
			||||||
		tf_ssb_db_init_reader(db);
 | 
							tf_ssb_db_init_reader(db);
 | 
				
			||||||
		tf_trace_sqlite(ssb->trace, db);
 | 
							tf_trace_sqlite(ssb->trace, db);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -2173,6 +2170,22 @@ void tf_ssb_release_db_reader(tf_ssb_t* ssb, sqlite3* db)
 | 
				
			|||||||
	uv_mutex_unlock(&ssb->db_readers_lock);
 | 
						uv_mutex_unlock(&ssb->db_readers_lock);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					sqlite3* tf_ssb_acquire_db_writer(tf_ssb_t* ssb)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						uv_mutex_lock(&ssb->db_writer_lock);
 | 
				
			||||||
 | 
						sqlite3* writer = ssb->db_writer;
 | 
				
			||||||
 | 
						assert(writer);
 | 
				
			||||||
 | 
						ssb->db_writer = NULL;
 | 
				
			||||||
 | 
						return writer;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void tf_ssb_release_db_writer(tf_ssb_t* ssb, sqlite3* db)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						assert(ssb->db_writer == NULL);
 | 
				
			||||||
 | 
						ssb->db_writer = db;
 | 
				
			||||||
 | 
						uv_mutex_unlock(&ssb->db_writer_lock);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
uv_loop_t* tf_ssb_get_loop(tf_ssb_t* ssb)
 | 
					uv_loop_t* tf_ssb_get_loop(tf_ssb_t* ssb)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	return ssb->loop;
 | 
						return ssb->loop;
 | 
				
			||||||
@@ -2205,10 +2218,12 @@ void tf_ssb_get_private_key(tf_ssb_t* ssb, uint8_t* out_private, size_t private_
 | 
				
			|||||||
void tf_ssb_set_trace(tf_ssb_t* ssb, tf_trace_t* trace)
 | 
					void tf_ssb_set_trace(tf_ssb_t* ssb, tf_trace_t* trace)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	ssb->trace = trace;
 | 
						ssb->trace = trace;
 | 
				
			||||||
	if (trace && ssb->db)
 | 
						sqlite3* db = tf_ssb_acquire_db_writer(ssb);
 | 
				
			||||||
 | 
						if (trace && db)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		tf_trace_sqlite(trace, ssb->db);
 | 
							tf_trace_sqlite(trace, db);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						tf_ssb_release_db_writer(ssb, db);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
tf_trace_t* tf_ssb_get_trace(tf_ssb_t* ssb)
 | 
					tf_trace_t* tf_ssb_get_trace(tf_ssb_t* ssb)
 | 
				
			||||||
@@ -2336,7 +2351,7 @@ void tf_ssb_destroy(tf_ssb_t* ssb)
 | 
				
			|||||||
		JS_FreeContext(ssb->context);
 | 
							JS_FreeContext(ssb->context);
 | 
				
			||||||
		JS_FreeRuntime(ssb->runtime);
 | 
							JS_FreeRuntime(ssb->runtime);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	sqlite3_close(ssb->db);
 | 
						sqlite3_close(ssb->db_writer);
 | 
				
			||||||
	while (ssb->broadcasts)
 | 
						while (ssb->broadcasts)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		tf_ssb_broadcast_t* broadcast = ssb->broadcasts;
 | 
							tf_ssb_broadcast_t* broadcast = ssb->broadcasts;
 | 
				
			||||||
@@ -2357,6 +2372,7 @@ void tf_ssb_destroy(tf_ssb_t* ssb)
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
	tf_free(ssb->db_readers);
 | 
						tf_free(ssb->db_readers);
 | 
				
			||||||
	uv_mutex_destroy(&ssb->db_readers_lock);
 | 
						uv_mutex_destroy(&ssb->db_readers_lock);
 | 
				
			||||||
 | 
						uv_mutex_destroy(&ssb->db_writer_lock);
 | 
				
			||||||
	tf_free((void*)ssb->db_path);
 | 
						tf_free((void*)ssb->db_path);
 | 
				
			||||||
	tf_free(ssb);
 | 
						tf_free(ssb);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -16,7 +16,6 @@
 | 
				
			|||||||
typedef struct _tf_ssb_connections_t
 | 
					typedef struct _tf_ssb_connections_t
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	tf_ssb_t* ssb;
 | 
						tf_ssb_t* ssb;
 | 
				
			||||||
	sqlite3* db;
 | 
					 | 
				
			||||||
	uv_timer_t timer;
 | 
						uv_timer_t timer;
 | 
				
			||||||
} tf_ssb_connections_t;
 | 
					} tf_ssb_connections_t;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -55,7 +54,8 @@ static bool _tf_ssb_connections_get_next_connection(tf_ssb_connections_t* connec
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
	bool result = false;
 | 
						bool result = false;
 | 
				
			||||||
	sqlite3_stmt* statement;
 | 
						sqlite3_stmt* statement;
 | 
				
			||||||
	if (sqlite3_prepare(connections->db, "SELECT host, port, key FROM connections WHERE last_attempt IS NULL OR (strftime('%s', 'now') - last_attempt > $1) ORDER BY last_attempt LIMIT 1", -1, &statement, NULL) == SQLITE_OK)
 | 
						sqlite3* db = tf_ssb_acquire_db_reader(connections->ssb);
 | 
				
			||||||
 | 
						if (sqlite3_prepare(db, "SELECT host, port, key FROM connections WHERE last_attempt IS NULL OR (strftime('%s', 'now') - last_attempt > $1) ORDER BY last_attempt LIMIT 1", -1, &statement, NULL) == SQLITE_OK)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		if (sqlite3_bind_int(statement, 1, 60000) == SQLITE_OK &&
 | 
							if (sqlite3_bind_int(statement, 1, 60000) == SQLITE_OK &&
 | 
				
			||||||
			sqlite3_step(statement) == SQLITE_ROW)
 | 
								sqlite3_step(statement) == SQLITE_ROW)
 | 
				
			||||||
@@ -69,8 +69,9 @@ static bool _tf_ssb_connections_get_next_connection(tf_ssb_connections_t* connec
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
	else
 | 
						else
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		tf_printf("prepare: %s\n", sqlite3_errmsg(connections->db));
 | 
							tf_printf("prepare: %s\n", sqlite3_errmsg(db));
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						tf_ssb_release_db_reader(connections->ssb, db);
 | 
				
			||||||
	return result;
 | 
						return result;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -100,7 +101,6 @@ tf_ssb_connections_t* tf_ssb_connections_create(tf_ssb_t* ssb)
 | 
				
			|||||||
	tf_ssb_connections_t* connections = tf_malloc(sizeof(tf_ssb_connections_t));
 | 
						tf_ssb_connections_t* connections = tf_malloc(sizeof(tf_ssb_connections_t));
 | 
				
			||||||
	memset(connections, 0, sizeof(*connections));
 | 
						memset(connections, 0, sizeof(*connections));
 | 
				
			||||||
	connections->ssb = ssb;
 | 
						connections->ssb = ssb;
 | 
				
			||||||
	connections->db = tf_ssb_get_db(ssb);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	tf_ssb_add_connections_changed_callback(ssb, _tf_ssb_connections_changed_callback, NULL, connections);
 | 
						tf_ssb_add_connections_changed_callback(ssb, _tf_ssb_connections_changed_callback, NULL, connections);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -128,7 +128,8 @@ void tf_ssb_connections_destroy(tf_ssb_connections_t* connections)
 | 
				
			|||||||
void tf_ssb_connections_store(tf_ssb_connections_t* connections, const char* host, int port, const char* key)
 | 
					void tf_ssb_connections_store(tf_ssb_connections_t* connections, const char* host, int port, const char* key)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	sqlite3_stmt* statement;
 | 
						sqlite3_stmt* statement;
 | 
				
			||||||
	if (sqlite3_prepare(connections->db, "INSERT INTO connections (host, port, key) VALUES ($1, $2, $3) ON CONFLICT DO NOTHING", -1, &statement, NULL) == SQLITE_OK)
 | 
						sqlite3* db = tf_ssb_acquire_db_writer(connections->ssb);
 | 
				
			||||||
 | 
						if (sqlite3_prepare(db, "INSERT INTO connections (host, port, key) VALUES ($1, $2, $3) ON CONFLICT DO NOTHING", -1, &statement, NULL) == SQLITE_OK)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		if (sqlite3_bind_text(statement, 1, host, -1, NULL) == SQLITE_OK &&
 | 
							if (sqlite3_bind_text(statement, 1, host, -1, NULL) == SQLITE_OK &&
 | 
				
			||||||
			sqlite3_bind_int(statement, 2, port) == SQLITE_OK &&
 | 
								sqlite3_bind_int(statement, 2, port) == SQLITE_OK &&
 | 
				
			||||||
@@ -137,17 +138,19 @@ void tf_ssb_connections_store(tf_ssb_connections_t* connections, const char* hos
 | 
				
			|||||||
			int r = sqlite3_step(statement);
 | 
								int r = sqlite3_step(statement);
 | 
				
			||||||
			if (r != SQLITE_DONE)
 | 
								if (r != SQLITE_DONE)
 | 
				
			||||||
			{
 | 
								{
 | 
				
			||||||
				tf_printf("tf_ssb_connections_store: %d, %s.\n", r, sqlite3_errmsg(connections->db));
 | 
									tf_printf("tf_ssb_connections_store: %d, %s.\n", r, sqlite3_errmsg(db));
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		sqlite3_finalize(statement);
 | 
							sqlite3_finalize(statement);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						tf_ssb_release_db_writer(connections->ssb, db);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void tf_ssb_connections_set_attempted(tf_ssb_connections_t* connections, const char* host, int port, const char* key)
 | 
					void tf_ssb_connections_set_attempted(tf_ssb_connections_t* connections, const char* host, int port, const char* key)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	sqlite3_stmt* statement;
 | 
						sqlite3_stmt* statement;
 | 
				
			||||||
	if (sqlite3_prepare(connections->db, "UPDATE connections SET last_attempt = strftime('%s', 'now') WHERE host = $1 AND port = $2 AND key = $3", -1, &statement, NULL) == SQLITE_OK)
 | 
						sqlite3* db = tf_ssb_acquire_db_writer(connections->ssb);
 | 
				
			||||||
 | 
						if (sqlite3_prepare(db, "UPDATE connections SET last_attempt = strftime('%s', 'now') WHERE host = $1 AND port = $2 AND key = $3", -1, &statement, NULL) == SQLITE_OK)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		if (sqlite3_bind_text(statement, 1, host, -1, NULL) == SQLITE_OK &&
 | 
							if (sqlite3_bind_text(statement, 1, host, -1, NULL) == SQLITE_OK &&
 | 
				
			||||||
			sqlite3_bind_int(statement, 2, port) == SQLITE_OK &&
 | 
								sqlite3_bind_int(statement, 2, port) == SQLITE_OK &&
 | 
				
			||||||
@@ -155,17 +158,19 @@ void tf_ssb_connections_set_attempted(tf_ssb_connections_t* connections, const c
 | 
				
			|||||||
		{
 | 
							{
 | 
				
			||||||
			if (sqlite3_step(statement) != SQLITE_DONE)
 | 
								if (sqlite3_step(statement) != SQLITE_DONE)
 | 
				
			||||||
			{
 | 
								{
 | 
				
			||||||
				tf_printf("tf_ssb_connections_set_attempted: %s.\n", sqlite3_errmsg(connections->db));
 | 
									tf_printf("tf_ssb_connections_set_attempted: %s.\n", sqlite3_errmsg(db));
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		sqlite3_finalize(statement);
 | 
							sqlite3_finalize(statement);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						tf_ssb_release_db_writer(connections->ssb, db);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void tf_ssb_connections_set_succeeded(tf_ssb_connections_t* connections, const char* host, int port, const char* key)
 | 
					void tf_ssb_connections_set_succeeded(tf_ssb_connections_t* connections, const char* host, int port, const char* key)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	sqlite3_stmt* statement;
 | 
						sqlite3_stmt* statement;
 | 
				
			||||||
	if (sqlite3_prepare(connections->db, "UPDATE connections SET last_success = strftime('%s', 'now') WHERE host = $1 AND port = $2 AND key = $3", -1, &statement, NULL) == SQLITE_OK)
 | 
						sqlite3* db = tf_ssb_acquire_db_writer(connections->ssb);
 | 
				
			||||||
 | 
						if (sqlite3_prepare(db, "UPDATE connections SET last_success = strftime('%s', 'now') WHERE host = $1 AND port = $2 AND key = $3", -1, &statement, NULL) == SQLITE_OK)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		if (sqlite3_bind_text(statement, 1, host, -1, NULL) == SQLITE_OK &&
 | 
							if (sqlite3_bind_text(statement, 1, host, -1, NULL) == SQLITE_OK &&
 | 
				
			||||||
			sqlite3_bind_int(statement, 2, port) == SQLITE_OK &&
 | 
								sqlite3_bind_int(statement, 2, port) == SQLITE_OK &&
 | 
				
			||||||
@@ -173,9 +178,10 @@ void tf_ssb_connections_set_succeeded(tf_ssb_connections_t* connections, const c
 | 
				
			|||||||
		{
 | 
							{
 | 
				
			||||||
			if (sqlite3_step(statement) != SQLITE_DONE)
 | 
								if (sqlite3_step(statement) != SQLITE_DONE)
 | 
				
			||||||
			{
 | 
								{
 | 
				
			||||||
				tf_printf("tf_ssb_connections_set_succeeded: %s.\n", sqlite3_errmsg(connections->db));
 | 
									tf_printf("tf_ssb_connections_set_succeeded: %s.\n", sqlite3_errmsg(db));
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		sqlite3_finalize(statement);
 | 
							sqlite3_finalize(statement);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						tf_ssb_release_db_writer(connections->ssb, db);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										69
									
								
								src/ssb.db.c
									
									
									
									
									
								
							
							
						
						
									
										69
									
								
								src/ssb.db.c
									
									
									
									
									
								
							@@ -67,7 +67,7 @@ void tf_ssb_db_init_reader(sqlite3* db)
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
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_acquire_db_writer(ssb);
 | 
				
			||||||
	_tf_ssb_db_init_internal(db);
 | 
						_tf_ssb_db_init_internal(db);
 | 
				
			||||||
	_tf_ssb_db_exec(db,
 | 
						_tf_ssb_db_exec(db,
 | 
				
			||||||
		"CREATE TABLE IF NOT EXISTS messages ("
 | 
							"CREATE TABLE IF NOT EXISTS messages ("
 | 
				
			||||||
@@ -222,6 +222,7 @@ void tf_ssb_db_init(tf_ssb_t* ssb)
 | 
				
			|||||||
		tf_printf("Adding sequence_before_author column.\n");
 | 
							tf_printf("Adding sequence_before_author column.\n");
 | 
				
			||||||
		_tf_ssb_db_exec(db, "ALTER TABLE messages ADD COLUMN sequence_before_author INTEGER");
 | 
							_tf_ssb_db_exec(db, "ALTER TABLE messages ADD COLUMN sequence_before_author INTEGER");
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						tf_ssb_release_db_writer(ssb, db);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
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)
 | 
				
			||||||
@@ -266,7 +267,7 @@ bool tf_ssb_db_store_message(tf_ssb_t* ssb, JSContext* context, const char* id,
 | 
				
			|||||||
	JS_ToInt64(context, &sequence, sequenceval);
 | 
						JS_ToInt64(context, &sequence, sequenceval);
 | 
				
			||||||
	JS_FreeValue(context, sequenceval);
 | 
						JS_FreeValue(context, sequenceval);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	sqlite3* db = tf_ssb_get_db(ssb);
 | 
						sqlite3* db = tf_ssb_acquire_db_writer(ssb);
 | 
				
			||||||
	sqlite3_stmt* statement;
 | 
						sqlite3_stmt* statement;
 | 
				
			||||||
	int64_t last_row_id = -1;
 | 
						int64_t last_row_id = -1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -315,7 +316,7 @@ bool tf_ssb_db_store_message(tf_ssb_t* ssb, JSContext* context, const char* id,
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
		else
 | 
							else
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			tf_printf("prepare failed: %s\n", sqlite3_errmsg(db));
 | 
								tf_printf("%s: prepare failed: %s\n", __FUNCTION__, sqlite3_errmsg(db));
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		JS_FreeCString(context, contentstr);
 | 
							JS_FreeCString(context, contentstr);
 | 
				
			||||||
@@ -348,9 +349,10 @@ bool tf_ssb_db_store_message(tf_ssb_t* ssb, JSContext* context, const char* id,
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
		else
 | 
							else
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			tf_printf("prepare failed: %s\n", sqlite3_errmsg(db));
 | 
								tf_printf("%s: prepare failed: %s\n", __FUNCTION__, sqlite3_errmsg(db));
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						tf_ssb_release_db_writer(ssb, db);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	JS_FreeCString(context, author);
 | 
						JS_FreeCString(context, author);
 | 
				
			||||||
	JS_FreeCString(context, previous);
 | 
						JS_FreeCString(context, previous);
 | 
				
			||||||
@@ -361,8 +363,9 @@ bool tf_ssb_db_message_content_get(tf_ssb_t* ssb, const char* id, uint8_t** out_
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
	bool result = false;
 | 
						bool result = false;
 | 
				
			||||||
	sqlite3_stmt* statement;
 | 
						sqlite3_stmt* statement;
 | 
				
			||||||
 | 
						sqlite3* db = tf_ssb_acquire_db_reader(ssb);
 | 
				
			||||||
	const char* query = "SELECT content FROM messages WHERE id = ?";
 | 
						const char* query = "SELECT content FROM messages WHERE id = ?";
 | 
				
			||||||
	if (sqlite3_prepare(tf_ssb_get_db(ssb), query, -1, &statement, NULL) == SQLITE_OK)
 | 
						if (sqlite3_prepare(db, query, -1, &statement, NULL) == SQLITE_OK)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		if (sqlite3_bind_text(statement, 1, id, -1, NULL) == SQLITE_OK &&
 | 
							if (sqlite3_bind_text(statement, 1, id, -1, NULL) == SQLITE_OK &&
 | 
				
			||||||
			sqlite3_step(statement) == SQLITE_ROW)
 | 
								sqlite3_step(statement) == SQLITE_ROW)
 | 
				
			||||||
@@ -383,6 +386,7 @@ bool tf_ssb_db_message_content_get(tf_ssb_t* ssb, const char* id, uint8_t** out_
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
		sqlite3_finalize(statement);
 | 
							sqlite3_finalize(statement);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						tf_ssb_release_db_reader(ssb, db);
 | 
				
			||||||
	return result;
 | 
						return result;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -390,8 +394,9 @@ bool tf_ssb_db_blob_has(tf_ssb_t* ssb, const char* id)
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
	bool result = false;
 | 
						bool result = false;
 | 
				
			||||||
	sqlite3_stmt* statement;
 | 
						sqlite3_stmt* statement;
 | 
				
			||||||
 | 
						sqlite3* db = tf_ssb_acquire_db_reader(ssb);
 | 
				
			||||||
	const char* query = "SELECT COUNT(*) FROM blobs WHERE id = $1";
 | 
						const char* query = "SELECT COUNT(*) FROM blobs WHERE id = $1";
 | 
				
			||||||
	if (sqlite3_prepare(tf_ssb_get_db(ssb), query, -1, &statement, NULL) == SQLITE_OK)
 | 
						if (sqlite3_prepare(db, query, -1, &statement, NULL) == SQLITE_OK)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		if (sqlite3_bind_text(statement, 1, id, -1, NULL) == SQLITE_OK &&
 | 
							if (sqlite3_bind_text(statement, 1, id, -1, NULL) == SQLITE_OK &&
 | 
				
			||||||
			sqlite3_step(statement) == SQLITE_ROW)
 | 
								sqlite3_step(statement) == SQLITE_ROW)
 | 
				
			||||||
@@ -400,6 +405,7 @@ bool tf_ssb_db_blob_has(tf_ssb_t* ssb, const char* id)
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
		sqlite3_finalize(statement);
 | 
							sqlite3_finalize(statement);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						tf_ssb_release_db_reader(ssb, db);
 | 
				
			||||||
	return result;
 | 
						return result;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -407,8 +413,9 @@ bool tf_ssb_db_blob_get(tf_ssb_t* ssb, const char* id, uint8_t** out_blob, size_
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
	bool result = false;
 | 
						bool result = false;
 | 
				
			||||||
	sqlite3_stmt* statement;
 | 
						sqlite3_stmt* statement;
 | 
				
			||||||
 | 
						sqlite3* db = tf_ssb_acquire_db_reader(ssb);
 | 
				
			||||||
	const char* query = "SELECT content FROM blobs WHERE id = $1";
 | 
						const char* query = "SELECT content FROM blobs WHERE id = $1";
 | 
				
			||||||
	if (sqlite3_prepare(tf_ssb_get_db(ssb), query, -1, &statement, NULL) == SQLITE_OK)
 | 
						if (sqlite3_prepare(db, query, -1, &statement, NULL) == SQLITE_OK)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		if (sqlite3_bind_text(statement, 1, id, -1, NULL) == SQLITE_OK &&
 | 
							if (sqlite3_bind_text(statement, 1, id, -1, NULL) == SQLITE_OK &&
 | 
				
			||||||
			sqlite3_step(statement) == SQLITE_ROW)
 | 
								sqlite3_step(statement) == SQLITE_ROW)
 | 
				
			||||||
@@ -432,13 +439,14 @@ bool tf_ssb_db_blob_get(tf_ssb_t* ssb, const char* id, uint8_t** out_blob, size_
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
		sqlite3_finalize(statement);
 | 
							sqlite3_finalize(statement);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						tf_ssb_release_db_reader(ssb, db);
 | 
				
			||||||
	return result;
 | 
						return result;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
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* out_new)
 | 
					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* out_new)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	bool result = false;
 | 
						bool result = false;
 | 
				
			||||||
	sqlite3* db = tf_ssb_get_db(ssb);
 | 
						sqlite3* db = tf_ssb_acquire_db_writer(ssb);
 | 
				
			||||||
	sqlite3_stmt* statement;
 | 
						sqlite3_stmt* statement;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	uint8_t hash[crypto_hash_sha256_BYTES];
 | 
						uint8_t hash[crypto_hash_sha256_BYTES];
 | 
				
			||||||
@@ -468,8 +476,9 @@ bool tf_ssb_db_blob_store(tf_ssb_t* ssb, const uint8_t* blob, size_t size, char*
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
	else
 | 
						else
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		tf_printf("prepare failed: %s\n", sqlite3_errmsg(db));
 | 
							tf_printf("%s: prepare failed: %s\n", __FUNCTION__, sqlite3_errmsg(db));
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						tf_ssb_release_db_writer(ssb, db);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (rows)
 | 
						if (rows)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
@@ -496,7 +505,8 @@ bool tf_ssb_db_get_message_by_author_and_sequence(tf_ssb_t* ssb, const char* aut
 | 
				
			|||||||
	bool found = false;
 | 
						bool found = false;
 | 
				
			||||||
	sqlite3_stmt* statement;
 | 
						sqlite3_stmt* statement;
 | 
				
			||||||
	const char* query = "SELECT id, timestamp, content FROM messages WHERE author = $1 AND sequence = $2";
 | 
						const char* query = "SELECT id, timestamp, content FROM messages WHERE author = $1 AND sequence = $2";
 | 
				
			||||||
	if (sqlite3_prepare(tf_ssb_get_db(ssb), query, -1, &statement, NULL) == SQLITE_OK)
 | 
						sqlite3* db = tf_ssb_acquire_db_reader(ssb);
 | 
				
			||||||
 | 
						if (sqlite3_prepare(db, query, -1, &statement, NULL) == SQLITE_OK)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		if (sqlite3_bind_text(statement, 1, author, -1, NULL) == SQLITE_OK &&
 | 
							if (sqlite3_bind_text(statement, 1, author, -1, NULL) == SQLITE_OK &&
 | 
				
			||||||
			sqlite3_bind_int64(statement, 2, sequence) == SQLITE_OK &&
 | 
								sqlite3_bind_int64(statement, 2, sequence) == SQLITE_OK &&
 | 
				
			||||||
@@ -520,8 +530,9 @@ bool tf_ssb_db_get_message_by_author_and_sequence(tf_ssb_t* ssb, const char* aut
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
	else
 | 
						else
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		tf_printf("prepare failed: %s\n", sqlite3_errmsg(tf_ssb_get_db(ssb)));
 | 
							tf_printf("%s: prepare failed: %s\n", __FUNCTION__, sqlite3_errmsg(db));
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						tf_ssb_release_db_reader(ssb, db);
 | 
				
			||||||
	return found;
 | 
						return found;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -529,8 +540,9 @@ bool tf_ssb_db_get_latest_message_by_author(tf_ssb_t* ssb, const char* author, i
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
	bool found = false;
 | 
						bool found = false;
 | 
				
			||||||
	sqlite3_stmt* statement;
 | 
						sqlite3_stmt* statement;
 | 
				
			||||||
 | 
						sqlite3* db = tf_ssb_acquire_db_reader(ssb);
 | 
				
			||||||
	const char* query = "SELECT id, sequence FROM messages WHERE author = $1 AND sequence = (SELECT MAX(sequence) FROM messages WHERE author = $1)";
 | 
						const char* query = "SELECT id, sequence FROM messages WHERE author = $1 AND sequence = (SELECT MAX(sequence) FROM messages WHERE author = $1)";
 | 
				
			||||||
	if (sqlite3_prepare(tf_ssb_get_db(ssb), query, -1, &statement, NULL) == SQLITE_OK)
 | 
						if (sqlite3_prepare(db, query, -1, &statement, NULL) == SQLITE_OK)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		if (sqlite3_bind_text(statement, 1, author, -1, NULL) == SQLITE_OK &&
 | 
							if (sqlite3_bind_text(statement, 1, author, -1, NULL) == SQLITE_OK &&
 | 
				
			||||||
			sqlite3_step(statement) == SQLITE_ROW)
 | 
								sqlite3_step(statement) == SQLITE_ROW)
 | 
				
			||||||
@@ -549,8 +561,9 @@ bool tf_ssb_db_get_latest_message_by_author(tf_ssb_t* ssb, const char* author, i
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
	else
 | 
						else
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		tf_printf("prepare failed: %s\n", sqlite3_errmsg(tf_ssb_get_db(ssb)));
 | 
							tf_printf("%s: prepare failed: %s\n", __FUNCTION__, sqlite3_errmsg(db));
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						tf_ssb_release_db_reader(ssb, db);
 | 
				
			||||||
	return found;
 | 
						return found;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -686,7 +699,7 @@ static int _tf_ssb_sqlite_authorizer(void* user_data, int action_code, const cha
 | 
				
			|||||||
JSValue 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)
 | 
					JSValue 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)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	JSValue result = JS_UNDEFINED;
 | 
						JSValue result = JS_UNDEFINED;
 | 
				
			||||||
	sqlite3* db = tf_ssb_get_db(ssb);
 | 
						sqlite3* db = tf_ssb_acquire_db_reader(ssb);
 | 
				
			||||||
	JSContext* context = tf_ssb_get_context(ssb);
 | 
						JSContext* context = tf_ssb_get_context(ssb);
 | 
				
			||||||
	sqlite3_stmt* statement;
 | 
						sqlite3_stmt* statement;
 | 
				
			||||||
	sqlite3_set_authorizer(db, _tf_ssb_sqlite_authorizer, ssb);
 | 
						sqlite3_set_authorizer(db, _tf_ssb_sqlite_authorizer, ssb);
 | 
				
			||||||
@@ -721,6 +734,7 @@ JSValue tf_ssb_db_visit_query(tf_ssb_t* ssb, const char* query, const JSValue bi
 | 
				
			|||||||
		result = JS_ThrowInternalError(context, "SQL Error %s: preparing \"%s\".", sqlite3_errmsg(db), query);
 | 
							result = JS_ThrowInternalError(context, "SQL Error %s: preparing \"%s\".", sqlite3_errmsg(db), query);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	sqlite3_set_authorizer(db, NULL, NULL);
 | 
						sqlite3_set_authorizer(db, NULL, NULL);
 | 
				
			||||||
 | 
						tf_ssb_release_db_reader(ssb, db);
 | 
				
			||||||
	return result;
 | 
						return result;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -874,7 +888,7 @@ bool tf_ssb_db_check(sqlite3* db, const char* check_author)
 | 
				
			|||||||
int tf_ssb_db_identity_get_count_for_user(tf_ssb_t* ssb, const char* user)
 | 
					int tf_ssb_db_identity_get_count_for_user(tf_ssb_t* ssb, const char* user)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	int count = 0;
 | 
						int count = 0;
 | 
				
			||||||
	sqlite3* db = tf_ssb_get_db(ssb);
 | 
						sqlite3* db = tf_ssb_acquire_db_reader(ssb);
 | 
				
			||||||
	sqlite3_stmt* statement = NULL;
 | 
						sqlite3_stmt* statement = NULL;
 | 
				
			||||||
	if (sqlite3_prepare(db, "SELECT COUNT(*) FROM identities WHERE user = ?", -1, &statement, NULL) == SQLITE_OK)
 | 
						if (sqlite3_prepare(db, "SELECT COUNT(*) FROM identities WHERE user = ?", -1, &statement, NULL) == SQLITE_OK)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
@@ -887,6 +901,7 @@ int tf_ssb_db_identity_get_count_for_user(tf_ssb_t* ssb, const char* user)
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
		sqlite3_finalize(statement);
 | 
							sqlite3_finalize(statement);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						tf_ssb_release_db_reader(ssb, db);
 | 
				
			||||||
	return count;
 | 
						return count;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -911,7 +926,7 @@ bool tf_ssb_db_identity_create(tf_ssb_t* ssb, const char* user, uint8_t* out_pub
 | 
				
			|||||||
bool tf_ssb_db_identity_add(tf_ssb_t* ssb, const char* user, const char* public_key, const char* private_key)
 | 
					bool tf_ssb_db_identity_add(tf_ssb_t* ssb, const char* user, const char* public_key, const char* private_key)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	bool added = false;
 | 
						bool added = false;
 | 
				
			||||||
	sqlite3* db = tf_ssb_get_db(ssb);
 | 
						sqlite3* db = tf_ssb_acquire_db_writer(ssb);
 | 
				
			||||||
	sqlite3_stmt* statement = NULL;
 | 
						sqlite3_stmt* statement = NULL;
 | 
				
			||||||
	if (sqlite3_prepare(db, "INSERT INTO identities (user, public_key, private_key) VALUES (?, ?, ?) ON CONFLICT DO NOTHING", -1, &statement, NULL) == SQLITE_OK)
 | 
						if (sqlite3_prepare(db, "INSERT INTO identities (user, public_key, private_key) VALUES (?, ?, ?) ON CONFLICT DO NOTHING", -1, &statement, NULL) == SQLITE_OK)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
@@ -929,12 +944,13 @@ bool tf_ssb_db_identity_add(tf_ssb_t* ssb, const char* user, const char* public_
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
		sqlite3_finalize(statement);
 | 
							sqlite3_finalize(statement);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						tf_ssb_release_db_writer(ssb, db);
 | 
				
			||||||
	return added;
 | 
						return added;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void tf_ssb_db_identity_visit(tf_ssb_t* ssb, const char* user, void (*callback)(const char* identity, void* user_data), void* user_data)
 | 
					void tf_ssb_db_identity_visit(tf_ssb_t* ssb, const char* user, void (*callback)(const char* identity, void* user_data), void* user_data)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	sqlite3* db = tf_ssb_get_db(ssb);
 | 
						sqlite3* db = tf_ssb_acquire_db_reader(ssb);
 | 
				
			||||||
	sqlite3_stmt* statement = NULL;
 | 
						sqlite3_stmt* statement = NULL;
 | 
				
			||||||
	if (sqlite3_prepare(db, "SELECT public_key FROM identities WHERE user = ? ORDER BY public_key", -1, &statement, NULL) == SQLITE_OK)
 | 
						if (sqlite3_prepare(db, "SELECT public_key FROM identities WHERE user = ? ORDER BY public_key", -1, &statement, NULL) == SQLITE_OK)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
@@ -947,11 +963,12 @@ void tf_ssb_db_identity_visit(tf_ssb_t* ssb, const char* user, void (*callback)(
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
		sqlite3_finalize(statement);
 | 
							sqlite3_finalize(statement);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						tf_ssb_release_db_reader(ssb, db);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void tf_ssb_db_identity_visit_all(tf_ssb_t* ssb, void (*callback)(const char* identity, void* user_data), void* user_data)
 | 
					void tf_ssb_db_identity_visit_all(tf_ssb_t* ssb, void (*callback)(const char* identity, void* user_data), void* user_data)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	sqlite3* db = tf_ssb_get_db(ssb);
 | 
						sqlite3* db = tf_ssb_acquire_db_reader(ssb);
 | 
				
			||||||
	sqlite3_stmt* statement = NULL;
 | 
						sqlite3_stmt* statement = NULL;
 | 
				
			||||||
	if (sqlite3_prepare(db, "SELECT public_key FROM identities ORDER BY public_key", -1, &statement, NULL) == SQLITE_OK)
 | 
						if (sqlite3_prepare(db, "SELECT public_key FROM identities ORDER BY public_key", -1, &statement, NULL) == SQLITE_OK)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
@@ -961,13 +978,14 @@ void tf_ssb_db_identity_visit_all(tf_ssb_t* ssb, void (*callback)(const char* id
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
		sqlite3_finalize(statement);
 | 
							sqlite3_finalize(statement);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						tf_ssb_release_db_reader(ssb, db);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bool tf_ssb_db_identity_get_private_key(tf_ssb_t* ssb, const char* user, const char* public_key, uint8_t* out_private_key, size_t private_key_size)
 | 
					bool tf_ssb_db_identity_get_private_key(tf_ssb_t* ssb, const char* user, const char* public_key, uint8_t* out_private_key, size_t private_key_size)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	bool success = false;
 | 
						bool success = false;
 | 
				
			||||||
	memset(out_private_key, 0, crypto_sign_SECRETKEYBYTES);
 | 
						memset(out_private_key, 0, crypto_sign_SECRETKEYBYTES);
 | 
				
			||||||
	sqlite3* db = tf_ssb_get_db(ssb);
 | 
						sqlite3* db = tf_ssb_acquire_db_reader(ssb);
 | 
				
			||||||
	sqlite3_stmt* statement = NULL;
 | 
						sqlite3_stmt* statement = NULL;
 | 
				
			||||||
	if (sqlite3_prepare(db, "SELECT private_key FROM identities WHERE user = ? AND public_key = ?", -1, &statement, NULL) == SQLITE_OK)
 | 
						if (sqlite3_prepare(db, "SELECT private_key FROM identities WHERE user = ? AND public_key = ?", -1, &statement, NULL) == SQLITE_OK)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
@@ -983,6 +1001,7 @@ bool tf_ssb_db_identity_get_private_key(tf_ssb_t* ssb, const char* user, const c
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
		sqlite3_finalize(statement);
 | 
							sqlite3_finalize(statement);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						tf_ssb_release_db_reader(ssb, db);
 | 
				
			||||||
	return success;
 | 
						return success;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -1050,7 +1069,7 @@ static following_t* _get_following(tf_ssb_t* ssb, const char* id, following_t***
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	if (depth < max_depth && !already_populated)
 | 
						if (depth < max_depth && !already_populated)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		sqlite3* db = tf_ssb_get_db(ssb);
 | 
							sqlite3* db = tf_ssb_acquire_db_reader(ssb);
 | 
				
			||||||
		sqlite3_stmt* statement = NULL;
 | 
							sqlite3_stmt* statement = NULL;
 | 
				
			||||||
		if (sqlite3_prepare(db, "SELECT json_extract(content, '$.contact'), json_extract(content, '$.following'), json_extract(content, '$.blocking') FROM messages WHERE author = ? AND json_extract(content, '$.type') = 'contact' ORDER BY sequence", -1, &statement, NULL) == SQLITE_OK)
 | 
							if (sqlite3_prepare(db, "SELECT json_extract(content, '$.contact'), json_extract(content, '$.following'), json_extract(content, '$.blocking') FROM messages WHERE author = ? AND json_extract(content, '$.type') = 'contact' ORDER BY sequence", -1, &statement, NULL) == SQLITE_OK)
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
@@ -1081,6 +1100,7 @@ static following_t* _get_following(tf_ssb_t* ssb, const char* id, following_t***
 | 
				
			|||||||
			}
 | 
								}
 | 
				
			||||||
			sqlite3_finalize(statement);
 | 
								sqlite3_finalize(statement);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
							tf_ssb_release_db_reader(ssb, db);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return entry;
 | 
						return entry;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -1147,7 +1167,7 @@ JSValue tf_ssb_db_get_message_by_id( tf_ssb_t* ssb, const char* id, bool is_keys
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
	JSValue result = JS_UNDEFINED;
 | 
						JSValue result = JS_UNDEFINED;
 | 
				
			||||||
	JSContext* context = tf_ssb_get_context(ssb);
 | 
						JSContext* context = tf_ssb_get_context(ssb);
 | 
				
			||||||
	sqlite3* db = tf_ssb_get_db(ssb);
 | 
						sqlite3* db = tf_ssb_acquire_db_reader(ssb);
 | 
				
			||||||
	sqlite3_stmt* statement;
 | 
						sqlite3_stmt* statement;
 | 
				
			||||||
	if (sqlite3_prepare(db, "SELECT previous, author, id, sequence, timestamp, hash, content, signature, sequence_before_author FROM messages WHERE id = ?", -1, &statement, NULL) == SQLITE_OK)
 | 
						if (sqlite3_prepare(db, "SELECT previous, author, id, sequence, timestamp, hash, content, signature, sequence_before_author FROM messages WHERE id = ?", -1, &statement, NULL) == SQLITE_OK)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
@@ -1182,12 +1202,13 @@ JSValue tf_ssb_db_get_message_by_id( tf_ssb_t* ssb, const char* id, bool is_keys
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
		sqlite3_finalize(statement);
 | 
							sqlite3_finalize(statement);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						tf_ssb_release_db_reader(ssb, db);
 | 
				
			||||||
	return result;
 | 
						return result;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
tf_ssb_db_stored_connection_t* tf_ssb_db_get_stored_connections(tf_ssb_t* ssb, int* out_count)
 | 
					tf_ssb_db_stored_connection_t* tf_ssb_db_get_stored_connections(tf_ssb_t* ssb, int* out_count)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	sqlite3* db = tf_ssb_get_db(ssb);
 | 
						sqlite3* db = tf_ssb_acquire_db_reader(ssb);
 | 
				
			||||||
	tf_ssb_db_stored_connection_t* result = NULL;
 | 
						tf_ssb_db_stored_connection_t* result = NULL;
 | 
				
			||||||
	int count = 0;
 | 
						int count = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -1207,6 +1228,7 @@ tf_ssb_db_stored_connection_t* tf_ssb_db_get_stored_connections(tf_ssb_t* ssb, i
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
		sqlite3_finalize(statement);
 | 
							sqlite3_finalize(statement);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						tf_ssb_release_db_reader(ssb, db);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	*out_count = count;
 | 
						*out_count = count;
 | 
				
			||||||
	return result;
 | 
						return result;
 | 
				
			||||||
@@ -1214,7 +1236,7 @@ tf_ssb_db_stored_connection_t* tf_ssb_db_get_stored_connections(tf_ssb_t* ssb, i
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
void tf_ssb_db_forget_stored_connection(tf_ssb_t* ssb, const char* address, int port, const char* pubkey)
 | 
					void tf_ssb_db_forget_stored_connection(tf_ssb_t* ssb, const char* address, int port, const char* pubkey)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	sqlite3* db = tf_ssb_get_db(ssb);
 | 
						sqlite3* db = tf_ssb_acquire_db_writer(ssb);
 | 
				
			||||||
	sqlite3_stmt* statement;
 | 
						sqlite3_stmt* statement;
 | 
				
			||||||
	if (sqlite3_prepare(db, "DELETE FROM connections WHERE host = ? AND port = ? AND key = ?", -1, &statement, NULL) == SQLITE_OK)
 | 
						if (sqlite3_prepare(db, "DELETE FROM connections WHERE host = ? AND port = ? AND key = ?", -1, &statement, NULL) == SQLITE_OK)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
@@ -1227,4 +1249,5 @@ void tf_ssb_db_forget_stored_connection(tf_ssb_t* ssb, const char* address, int
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
		sqlite3_finalize(statement);
 | 
							sqlite3_finalize(statement);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						tf_ssb_release_db_writer(ssb, db);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -87,9 +87,10 @@ void tf_ssb_export(tf_ssb_t* ssb, const char* key)
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	char app_blob_id[64] = { 0 };
 | 
						char app_blob_id[64] = { 0 };
 | 
				
			||||||
	sqlite3_busy_timeout(tf_ssb_get_db(ssb), 10000);
 | 
						sqlite3* db = tf_ssb_acquire_db_reader(ssb);
 | 
				
			||||||
 | 
						sqlite3_busy_timeout(db, 10000);
 | 
				
			||||||
	sqlite3_stmt* statement;
 | 
						sqlite3_stmt* statement;
 | 
				
			||||||
	if (sqlite3_prepare(tf_ssb_get_db(ssb), "SELECT value FROM properties WHERE id = $1 AND key = 'path:' || $2", -1, &statement, NULL) == SQLITE_OK)
 | 
						if (sqlite3_prepare(db, "SELECT value FROM properties WHERE id = $1 AND key = 'path:' || $2", -1, &statement, NULL) == SQLITE_OK)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		if (sqlite3_bind_text(statement, 1, user, -1, NULL) == SQLITE_OK &&
 | 
							if (sqlite3_bind_text(statement, 1, user, -1, NULL) == SQLITE_OK &&
 | 
				
			||||||
			sqlite3_bind_text(statement, 2, path, -1, NULL) == SQLITE_OK &&
 | 
								sqlite3_bind_text(statement, 2, path, -1, NULL) == SQLITE_OK &&
 | 
				
			||||||
@@ -105,6 +106,7 @@ void tf_ssb_export(tf_ssb_t* ssb, const char* key)
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
		sqlite3_finalize(statement);
 | 
							sqlite3_finalize(statement);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						tf_ssb_release_db_reader(ssb, db);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!*app_blob_id)
 | 
						if (!*app_blob_id)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -69,9 +69,10 @@ typedef struct _tf_ssb_blob_wants_t
 | 
				
			|||||||
tf_ssb_t* tf_ssb_create(uv_loop_t* loop, JSContext* context, const char* db_path);
 | 
					tf_ssb_t* tf_ssb_create(uv_loop_t* loop, JSContext* context, const char* db_path);
 | 
				
			||||||
void tf_ssb_destroy(tf_ssb_t* ssb);
 | 
					void tf_ssb_destroy(tf_ssb_t* ssb);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
sqlite3* tf_ssb_get_db(tf_ssb_t* ssb);
 | 
					 | 
				
			||||||
sqlite3* tf_ssb_acquire_db_reader(tf_ssb_t* ssb);
 | 
					sqlite3* tf_ssb_acquire_db_reader(tf_ssb_t* ssb);
 | 
				
			||||||
void tf_ssb_release_db_reader(tf_ssb_t* ssb, sqlite3* db);
 | 
					void tf_ssb_release_db_reader(tf_ssb_t* ssb, sqlite3* db);
 | 
				
			||||||
 | 
					sqlite3* tf_ssb_acquire_db_writer(tf_ssb_t* ssb);
 | 
				
			||||||
 | 
					void tf_ssb_release_db_writer(tf_ssb_t* ssb, sqlite3* db);
 | 
				
			||||||
uv_loop_t* tf_ssb_get_loop(tf_ssb_t* ssb);
 | 
					uv_loop_t* tf_ssb_get_loop(tf_ssb_t* ssb);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void tf_ssb_generate_keys(tf_ssb_t* ssb);
 | 
					void tf_ssb_generate_keys(tf_ssb_t* ssb);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -20,7 +20,8 @@ static void _tf_ssb_import_add_app(tf_ssb_t* ssb, const char* user, const char*
 | 
				
			|||||||
	sqlite3_stmt* statement;
 | 
						sqlite3_stmt* statement;
 | 
				
			||||||
	JSContext* context = tf_ssb_get_context(ssb);
 | 
						JSContext* context = tf_ssb_get_context(ssb);
 | 
				
			||||||
	JSValue apps = JS_UNDEFINED;
 | 
						JSValue apps = JS_UNDEFINED;
 | 
				
			||||||
	if (sqlite3_prepare(tf_ssb_get_db(ssb), "SELECT value FROM properties WHERE id = $1 AND key = 'apps'", -1, &statement, NULL) == SQLITE_OK)
 | 
						sqlite3* db = tf_ssb_acquire_db_writer(ssb);
 | 
				
			||||||
 | 
						if (sqlite3_prepare(db, "SELECT value FROM properties WHERE id = $1 AND key = 'apps'", -1, &statement, NULL) == SQLITE_OK)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		if (sqlite3_bind_text(statement, 1, user, -1, NULL) == SQLITE_OK &&
 | 
							if (sqlite3_bind_text(statement, 1, user, -1, NULL) == SQLITE_OK &&
 | 
				
			||||||
			sqlite3_step(statement) == SQLITE_ROW)
 | 
								sqlite3_step(statement) == SQLITE_ROW)
 | 
				
			||||||
@@ -68,7 +69,7 @@ static void _tf_ssb_import_add_app(tf_ssb_t* ssb, const char* user, const char*
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	JSValue json = JS_JSONStringify(context, out_apps, JS_NULL, JS_NULL);
 | 
						JSValue json = JS_JSONStringify(context, out_apps, JS_NULL, JS_NULL);
 | 
				
			||||||
	const char* text = JS_ToCString(context, json);
 | 
						const char* text = JS_ToCString(context, json);
 | 
				
			||||||
	if (sqlite3_prepare(tf_ssb_get_db(ssb), "INSERT OR REPLACE INTO properties (id, key, value) VALUES ($1, 'apps', $2)", -1, &statement, NULL) == SQLITE_OK)
 | 
						if (sqlite3_prepare(db, "INSERT OR REPLACE INTO properties (id, key, value) VALUES ($1, 'apps', $2)", -1, &statement, NULL) == SQLITE_OK)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		if (sqlite3_bind_text(statement, 1, user, -1, NULL) == SQLITE_OK &&
 | 
							if (sqlite3_bind_text(statement, 1, user, -1, NULL) == SQLITE_OK &&
 | 
				
			||||||
			sqlite3_bind_text(statement, 2, text, -1, NULL) == SQLITE_OK &&
 | 
								sqlite3_bind_text(statement, 2, text, -1, NULL) == SQLITE_OK &&
 | 
				
			||||||
@@ -77,6 +78,7 @@ static void _tf_ssb_import_add_app(tf_ssb_t* ssb, const char* user, const char*
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
		sqlite3_finalize(statement);
 | 
							sqlite3_finalize(statement);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						tf_ssb_release_db_writer(ssb, db);
 | 
				
			||||||
	JS_FreeCString(context, text);
 | 
						JS_FreeCString(context, text);
 | 
				
			||||||
	JS_FreeValue(context, json);
 | 
						JS_FreeValue(context, json);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -161,17 +163,19 @@ static bool _tf_ssb_register_app(tf_ssb_t* ssb, const char* user, const char* ap
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
	bool result = false;
 | 
						bool result = false;
 | 
				
			||||||
	sqlite3_stmt* statement;
 | 
						sqlite3_stmt* statement;
 | 
				
			||||||
	 if (sqlite3_prepare(tf_ssb_get_db(ssb), "INSERT OR REPLACE INTO properties (id, key, value) VALUES ($1, 'path:' || $2, $3)", -1, &statement, NULL) == SQLITE_OK)
 | 
						sqlite3* db = tf_ssb_acquire_db_writer(ssb);
 | 
				
			||||||
 | 
						if (sqlite3_prepare(db, "INSERT OR REPLACE INTO properties (id, key, value) VALUES ($1, 'path:' || $2, $3)", -1, &statement, NULL) == SQLITE_OK)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		if (sqlite3_bind_text(statement, 1, user, -1, NULL) == SQLITE_OK &&
 | 
							if (sqlite3_bind_text(statement, 1, user, -1, NULL) == SQLITE_OK &&
 | 
				
			||||||
				sqlite3_bind_text(statement, 2, app, -1, NULL) == SQLITE_OK &&
 | 
									sqlite3_bind_text(statement, 2, app, -1, NULL) == SQLITE_OK &&
 | 
				
			||||||
				sqlite3_bind_text(statement, 3, id, -1, NULL) == SQLITE_OK &&
 | 
									sqlite3_bind_text(statement, 3, id, -1, NULL) == SQLITE_OK &&
 | 
				
			||||||
				sqlite3_step(statement) == SQLITE_DONE)
 | 
									sqlite3_step(statement) == SQLITE_DONE)
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
				result = sqlite3_changes(tf_ssb_get_db(ssb)) != 0;
 | 
									result = sqlite3_changes(db) != 0;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		sqlite3_finalize(statement);
 | 
							sqlite3_finalize(statement);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						tf_ssb_release_db_writer(ssb, db);
 | 
				
			||||||
	return result;
 | 
						return result;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -229,7 +233,6 @@ static void _tf_ssb_import_app_json(tf_ssb_t* ssb, uv_loop_t* loop, JSContext* c
 | 
				
			|||||||
void tf_ssb_import(tf_ssb_t* ssb, const char* user, const char* path)
 | 
					void tf_ssb_import(tf_ssb_t* ssb, const char* user, const char* path)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	uv_fs_t req = { 0 };
 | 
						uv_fs_t req = { 0 };
 | 
				
			||||||
	sqlite3_busy_timeout(tf_ssb_get_db(ssb), 10000);
 | 
					 | 
				
			||||||
	int r = uv_fs_scandir(tf_ssb_get_loop(ssb), &req, path, 0, NULL);
 | 
						int r = uv_fs_scandir(tf_ssb_get_loop(ssb), &req, path, 0, NULL);
 | 
				
			||||||
	if (r >= 0)
 | 
						if (r >= 0)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1091,7 +1091,7 @@ static JSValue _tf_ssb_private_message_encrypt(JSContext* context, JSValueConst
 | 
				
			|||||||
	if (JS_IsUndefined(result))
 | 
						if (JS_IsUndefined(result))
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		tf_ssb_t* ssb = JS_GetOpaque(this_val, _tf_ssb_classId);
 | 
							tf_ssb_t* ssb = JS_GetOpaque(this_val, _tf_ssb_classId);
 | 
				
			||||||
		sqlite3* db = tf_ssb_get_db(ssb);
 | 
							sqlite3* db = tf_ssb_acquire_db_reader(ssb);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		uint8_t private_key[crypto_sign_SECRETKEYBYTES] = { 0 };
 | 
							uint8_t private_key[crypto_sign_SECRETKEYBYTES] = { 0 };
 | 
				
			||||||
		if (_tf_ssb_get_private_key_curve25519(db, signer_user, signer_identity, private_key))
 | 
							if (_tf_ssb_get_private_key_curve25519(db, signer_user, signer_identity, private_key))
 | 
				
			||||||
@@ -1167,6 +1167,7 @@ static JSValue _tf_ssb_private_message_encrypt(JSContext* context, JSValueConst
 | 
				
			|||||||
		{
 | 
							{
 | 
				
			||||||
			result = JS_ThrowInternalError(context, "Unable to get key for ID %s of user %s.", signer_identity, signer_user);
 | 
								result = JS_ThrowInternalError(context, "Unable to get key for ID %s of user %s.", signer_identity, signer_user);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
							tf_ssb_release_db_reader(ssb, db);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	JS_FreeCString(context, signer_user);
 | 
						JS_FreeCString(context, signer_user);
 | 
				
			||||||
@@ -1187,7 +1188,7 @@ static JSValue _tf_ssb_private_message_decrypt(JSContext* context, JSValueConst
 | 
				
			|||||||
	uint8_t private_key[crypto_sign_SECRETKEYBYTES] = { 0 };
 | 
						uint8_t private_key[crypto_sign_SECRETKEYBYTES] = { 0 };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	tf_ssb_t* ssb = JS_GetOpaque(this_val, _tf_ssb_classId);
 | 
						tf_ssb_t* ssb = JS_GetOpaque(this_val, _tf_ssb_classId);
 | 
				
			||||||
	sqlite3* db = tf_ssb_get_db(ssb);
 | 
						sqlite3* db = tf_ssb_acquire_db_reader(ssb);
 | 
				
			||||||
	if (_tf_ssb_get_private_key_curve25519(db, user, identity, private_key))
 | 
						if (_tf_ssb_get_private_key_curve25519(db, user, identity, private_key))
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		uint8_t* decoded = tf_malloc(message_size);
 | 
							uint8_t* decoded = tf_malloc(message_size);
 | 
				
			||||||
@@ -1238,6 +1239,7 @@ static JSValue _tf_ssb_private_message_decrypt(JSContext* context, JSValueConst
 | 
				
			|||||||
	{
 | 
						{
 | 
				
			||||||
		result = JS_ThrowInternalError(context, "Private key not found for user %s with id %s.", user, identity);
 | 
							result = JS_ThrowInternalError(context, "Private key not found for user %s with id %s.", user, identity);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						tf_ssb_release_db_reader(ssb, db);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	JS_FreeCString(context, user);
 | 
						JS_FreeCString(context, user);
 | 
				
			||||||
	JS_FreeCString(context, identity);
 | 
						JS_FreeCString(context, identity);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -141,7 +141,7 @@ static void _tf_ssb_rpc_request_more_blobs(tf_ssb_connection_t* connection)
 | 
				
			|||||||
	JSContext* context = tf_ssb_connection_get_context(connection);
 | 
						JSContext* context = tf_ssb_connection_get_context(connection);
 | 
				
			||||||
	tf_ssb_blob_wants_t* blob_wants = tf_ssb_connection_get_blob_wants_state(connection);
 | 
						tf_ssb_blob_wants_t* blob_wants = tf_ssb_connection_get_blob_wants_state(connection);
 | 
				
			||||||
	tf_ssb_t* ssb = tf_ssb_connection_get_ssb(connection);
 | 
						tf_ssb_t* ssb = tf_ssb_connection_get_ssb(connection);
 | 
				
			||||||
	sqlite3* db = tf_ssb_get_db(ssb);
 | 
						sqlite3* db = tf_ssb_acquire_db_reader(ssb);
 | 
				
			||||||
	sqlite3_stmt* statement;
 | 
						sqlite3_stmt* statement;
 | 
				
			||||||
	if (sqlite3_prepare(db, "SELECT id FROM blob_wants_view WHERE id > ? ORDER BY id LIMIT 32", -1, &statement, NULL) == SQLITE_OK)
 | 
						if (sqlite3_prepare(db, "SELECT id FROM blob_wants_view WHERE id > ? ORDER BY id LIMIT 32", -1, &statement, NULL) == SQLITE_OK)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
@@ -160,6 +160,7 @@ static void _tf_ssb_rpc_request_more_blobs(tf_ssb_connection_t* connection)
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
		sqlite3_finalize(statement);
 | 
							sqlite3_finalize(statement);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						tf_ssb_release_db_reader(ssb, db);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void _tf_ssb_rpc_blobs_createWants(tf_ssb_connection_t* connection, uint8_t flags, int32_t request_number, JSValue args, const uint8_t* message, size_t size, void* user_data)
 | 
					static void _tf_ssb_rpc_blobs_createWants(tf_ssb_connection_t* connection, uint8_t flags, int32_t request_number, JSValue args, const uint8_t* message, size_t size, void* user_data)
 | 
				
			||||||
@@ -192,7 +193,7 @@ static bool _get_global_setting_bool(tf_ssb_t* ssb, const char* name, bool defau
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
	bool result = default_value;
 | 
						bool result = default_value;
 | 
				
			||||||
	JSContext* context = tf_ssb_get_context(ssb);
 | 
						JSContext* context = tf_ssb_get_context(ssb);
 | 
				
			||||||
	sqlite3* db = tf_ssb_get_db(ssb);
 | 
						sqlite3* db = tf_ssb_acquire_db_reader(ssb);
 | 
				
			||||||
	sqlite3_stmt* statement;
 | 
						sqlite3_stmt* statement;
 | 
				
			||||||
	if (sqlite3_prepare(db, "SELECT value FROM properties WHERE id = 'core' AND key = 'settings'", -1, &statement, NULL) == SQLITE_OK)
 | 
						if (sqlite3_prepare(db, "SELECT value FROM properties WHERE id = 'core' AND key = 'settings'", -1, &statement, NULL) == SQLITE_OK)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
@@ -209,6 +210,7 @@ static bool _get_global_setting_bool(tf_ssb_t* ssb, const char* name, bool defau
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
		sqlite3_finalize(statement);
 | 
							sqlite3_finalize(statement);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						tf_ssb_release_db_reader(ssb, db);
 | 
				
			||||||
	return result;
 | 
						return result;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -216,7 +218,7 @@ static bool _get_global_setting_string(tf_ssb_t* ssb, const char* name, char* ou
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
	bool result = false;
 | 
						bool result = false;
 | 
				
			||||||
	JSContext* context = tf_ssb_get_context(ssb);
 | 
						JSContext* context = tf_ssb_get_context(ssb);
 | 
				
			||||||
	sqlite3* db = tf_ssb_get_db(ssb);
 | 
						sqlite3* db = tf_ssb_acquire_db_reader(ssb);
 | 
				
			||||||
	sqlite3_stmt* statement;
 | 
						sqlite3_stmt* statement;
 | 
				
			||||||
	if (sqlite3_prepare(db, "SELECT value FROM properties WHERE id = 'core' AND key = 'settings'", -1, &statement, NULL) == SQLITE_OK)
 | 
						if (sqlite3_prepare(db, "SELECT value FROM properties WHERE id = 'core' AND key = 'settings'", -1, &statement, NULL) == SQLITE_OK)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
@@ -236,6 +238,7 @@ static bool _get_global_setting_string(tf_ssb_t* ssb, const char* name, char* ou
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
		sqlite3_finalize(statement);
 | 
							sqlite3_finalize(statement);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						tf_ssb_release_db_reader(ssb, db);
 | 
				
			||||||
	return result;
 | 
						return result;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -698,7 +701,7 @@ static void _tf_ssb_connection_send_history_stream_internal(tf_ssb_connection_t*
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
	tf_ssb_t* ssb = tf_ssb_connection_get_ssb(connection);
 | 
						tf_ssb_t* ssb = tf_ssb_connection_get_ssb(connection);
 | 
				
			||||||
	JSContext* context = tf_ssb_get_context(ssb);
 | 
						JSContext* context = tf_ssb_get_context(ssb);
 | 
				
			||||||
	sqlite3* db = tf_ssb_get_db(ssb);
 | 
						sqlite3* db = tf_ssb_acquire_db_reader(ssb);
 | 
				
			||||||
	sqlite3_stmt* statement;
 | 
						sqlite3_stmt* statement;
 | 
				
			||||||
	const int k_max = 32;
 | 
						const int k_max = 32;
 | 
				
			||||||
	int64_t max_sequence_seen = 0;
 | 
						int64_t max_sequence_seen = 0;
 | 
				
			||||||
@@ -740,6 +743,7 @@ static void _tf_ssb_connection_send_history_stream_internal(tf_ssb_connection_t*
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
		sqlite3_finalize(statement);
 | 
							sqlite3_finalize(statement);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						tf_ssb_release_db_reader(ssb, db);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (max_sequence_seen == sequence + k_max - 1)
 | 
						if (max_sequence_seen == sequence + k_max - 1)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -127,9 +127,9 @@ void tf_ssb_test_ssb(const tf_test_options_t* options)
 | 
				
			|||||||
	uv_loop_t loop = { 0 };
 | 
						uv_loop_t loop = { 0 };
 | 
				
			||||||
	uv_loop_init(&loop);
 | 
						uv_loop_init(&loop);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	tf_ssb_t* ssb0 = tf_ssb_create(&loop, NULL, ":memory:");
 | 
						tf_ssb_t* ssb0 = tf_ssb_create(&loop, NULL, "file:db0?mode=memory&cache=shared");
 | 
				
			||||||
	tf_ssb_register(tf_ssb_get_context(ssb0), ssb0);
 | 
						tf_ssb_register(tf_ssb_get_context(ssb0), ssb0);
 | 
				
			||||||
	tf_ssb_t* ssb1 = tf_ssb_create(&loop, NULL, ":memory:");
 | 
						tf_ssb_t* ssb1 = tf_ssb_create(&loop, NULL, "file:db1?mode=memory&cache=shared");
 | 
				
			||||||
	tf_ssb_register(tf_ssb_get_context(ssb1), ssb1);
 | 
						tf_ssb_register(tf_ssb_get_context(ssb1), ssb1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	uv_idle_t idle0 = { .data = ssb0 };
 | 
						uv_idle_t idle0 = { .data = ssb0 };
 | 
				
			||||||
@@ -311,11 +311,11 @@ void tf_ssb_test_rooms(const tf_test_options_t* options)
 | 
				
			|||||||
	uv_loop_t loop = { 0 };
 | 
						uv_loop_t loop = { 0 };
 | 
				
			||||||
	uv_loop_init(&loop);
 | 
						uv_loop_init(&loop);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	tf_ssb_t* ssb0 = tf_ssb_create(&loop, NULL, ":memory:");
 | 
						tf_ssb_t* ssb0 = tf_ssb_create(&loop, NULL, "file:db0?mode=memory&cache=shared");
 | 
				
			||||||
	tf_ssb_register(tf_ssb_get_context(ssb0), ssb0);
 | 
						tf_ssb_register(tf_ssb_get_context(ssb0), ssb0);
 | 
				
			||||||
	tf_ssb_t* ssb1 = tf_ssb_create(&loop, NULL, ":memory:");
 | 
						tf_ssb_t* ssb1 = tf_ssb_create(&loop, NULL, "file:db1?mode=memory&cache=shared");
 | 
				
			||||||
	tf_ssb_register(tf_ssb_get_context(ssb1), ssb1);
 | 
						tf_ssb_register(tf_ssb_get_context(ssb1), ssb1);
 | 
				
			||||||
	tf_ssb_t* ssb2 = tf_ssb_create(&loop, NULL, ":memory:");
 | 
						tf_ssb_t* ssb2 = tf_ssb_create(&loop, NULL, "file:db2?mode=memory&cache=shared");
 | 
				
			||||||
	tf_ssb_register(tf_ssb_get_context(ssb2), ssb2);
 | 
						tf_ssb_register(tf_ssb_get_context(ssb2), ssb2);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	uv_idle_t idle0 = { .data = ssb0 };
 | 
						uv_idle_t idle0 = { .data = ssb0 };
 | 
				
			||||||
@@ -555,7 +555,7 @@ void tf_ssb_test_bench(const tf_test_options_t* options)
 | 
				
			|||||||
	uv_loop_t loop = { 0 };
 | 
						uv_loop_t loop = { 0 };
 | 
				
			||||||
	uv_loop_init(&loop);
 | 
						uv_loop_init(&loop);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	tf_ssb_t* ssb0 = tf_ssb_create(&loop, NULL, ":memory:");
 | 
						tf_ssb_t* ssb0 = tf_ssb_create(&loop, NULL, "file:db0?mode=memory&cache=shared");
 | 
				
			||||||
	tf_ssb_generate_keys(ssb0);
 | 
						tf_ssb_generate_keys(ssb0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	char id0[k_id_base64_len] = { 0 };
 | 
						char id0[k_id_base64_len] = { 0 };
 | 
				
			||||||
@@ -579,7 +579,7 @@ void tf_ssb_test_bench(const tf_test_options_t* options)
 | 
				
			|||||||
	clock_gettime(CLOCK_REALTIME, &end_time);
 | 
						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);
 | 
						tf_printf("insert = %f seconds\n", (end_time.tv_sec - start_time.tv_sec) + (end_time.tv_nsec - start_time.tv_nsec) / 1e9);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	tf_ssb_t* ssb1 = tf_ssb_create(&loop, NULL, ":memory:");
 | 
						tf_ssb_t* ssb1 = tf_ssb_create(&loop, NULL, "file:db1?mode=memory&cache=shared");
 | 
				
			||||||
	tf_ssb_generate_keys(ssb1);
 | 
						tf_ssb_generate_keys(ssb1);
 | 
				
			||||||
	uint8_t id0bin[k_id_bin_len];
 | 
						uint8_t id0bin[k_id_bin_len];
 | 
				
			||||||
	tf_ssb_id_str_to_bin(id0bin, id0);
 | 
						tf_ssb_id_str_to_bin(id0bin, id0);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -236,7 +236,7 @@ static void _test_database(const tf_test_options_t* options)
 | 
				
			|||||||
	unlink("out/testdb.sqlite");
 | 
						unlink("out/testdb.sqlite");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	char command[256];
 | 
						char command[256];
 | 
				
			||||||
	snprintf(command, sizeof(command), "%s run --ssb-port=0 --db-path=:memory: --db-path=out/testdb.sqlite -s out/test.js", options->exe_path);
 | 
						snprintf(command, sizeof(command), "%s run --ssb-port=0 --db-path=file:db?mode=memory\\&cache=shared -s out/test.js", options->exe_path);
 | 
				
			||||||
	tf_printf("%s\n", command);
 | 
						tf_printf("%s\n", command);
 | 
				
			||||||
	int result = system(command);
 | 
						int result = system(command);
 | 
				
			||||||
	tf_printf("returned %d\n", WEXITSTATUS(result));
 | 
						tf_printf("returned %d\n", WEXITSTATUS(result));
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user