Return peer lists atomically after successful SQL iteration
This commit is contained in:
1 parent
575a3788e3
commit
81e2a9aa4a
2 files changed
+81
-74
No files matched your search
@@ -1395,98 +1395,90 @@ int topo_node_sqlite_get_connect_peers(sqlite3* db, const char* channel_id,
|
||||
sqlite3_finalize(stmt); *out = peers; *out_count = count; return 0;
|
||||
}
|
||||
|
||||
int topo_node_sqlite_get_connected_peers(sqlite3* db, const char* channel_id,
|
||||
uint64_t** out_ids, int* out_count) {
|
||||
if (!db || !channel_id || !out_ids || !out_count) return -1;
|
||||
/* Один проход SQL: частичный массив никогда не публикуется при ошибке чтения/памяти. */
|
||||
static int node_ids_query(sqlite3* db, const char* sql, int exclude_self, uint64_t self,
|
||||
uint64_t** out_ids, int* out_count) {
|
||||
*out_ids = NULL; *out_count = 0;
|
||||
char peers_tbl[80]; peers_table_name(channel_id, peers_tbl, sizeof(peers_tbl));
|
||||
char sql[192]; snprintf(sql, sizeof(sql), "SELECT node_id FROM \"%s\" WHERE connected=1 AND deleted=0", peers_tbl);
|
||||
sqlite3_stmt* st = NULL;
|
||||
if (sqlite3_prepare_v2(db, sql, -1, &st, NULL) != SQLITE_OK) return -1;
|
||||
int cnt = 0;
|
||||
while (sqlite3_step(st) == SQLITE_ROW) cnt++;
|
||||
if (cnt == 0) { sqlite3_finalize(st); return 0; }
|
||||
uint64_t* ids = u_malloc((size_t)cnt * sizeof(uint64_t));
|
||||
if (!ids) { sqlite3_finalize(st); return -1; }
|
||||
sqlite3_reset(st); int i = 0;
|
||||
while (sqlite3_step(st) == SQLITE_ROW) ids[i++] = (uint64_t)sqlite3_column_int64(st, 0);
|
||||
uint64_t* ids = NULL;
|
||||
size_t count = 0, capacity = 0;
|
||||
int rc = sqlite3_prepare_v2(db, sql, -1, &st, NULL);
|
||||
if (rc == SQLITE_OK && exclude_self) rc = sqlite3_bind_int64(st, 1, (sqlite3_int64)self);
|
||||
if (rc != SQLITE_OK) goto fail;
|
||||
while ((rc = sqlite3_step(st)) == SQLITE_ROW) {
|
||||
if (count == capacity) {
|
||||
size_t next = capacity ? capacity * 2 : 16;
|
||||
if (next > UINT32_MAX / sizeof(*ids)) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "peer ID list too large: count=%zu", count); goto fail;
|
||||
}
|
||||
uint64_t* resized = u_realloc(ids, next * sizeof(*ids));
|
||||
if (!resized) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "peer ID list allocation failed: count=%zu", count); goto fail; }
|
||||
ids = resized; capacity = next;
|
||||
}
|
||||
ids[count++] = (uint64_t)sqlite3_column_int64(st, 0);
|
||||
}
|
||||
if (rc != SQLITE_DONE) goto fail;
|
||||
sqlite3_finalize(st);
|
||||
*out_ids = ids; *out_count = cnt;
|
||||
*out_ids = ids; *out_count = (int)count;
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_BGP, "peer ID list read: count=%zu exclude_self=%d", count, exclude_self);
|
||||
return 0;
|
||||
fail:
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "peer ID query failed: rc=%d rows=%zu sql=%s: %s", rc, count, sql, sqlite3_errmsg(db));
|
||||
sqlite3_finalize(st); u_free(ids); return -1;
|
||||
}
|
||||
|
||||
int topo_node_sqlite_get_connected_peers(sqlite3* db, const char* channel_id, uint64_t** out_ids, int* out_count) {
|
||||
if (out_ids) *out_ids = NULL;
|
||||
if (out_count) *out_count = 0;
|
||||
if (!db || !channel_id || !out_ids || !out_count) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "connected peers: invalid arguments"); return -1;
|
||||
}
|
||||
char table[80], sql[192]; peers_table_name(channel_id, table, sizeof(table));
|
||||
snprintf(sql, sizeof(sql), "SELECT node_id FROM \"%s\" WHERE connected=1 AND deleted=0", table);
|
||||
return node_ids_query(db, sql, 0, 0, out_ids, out_count);
|
||||
}
|
||||
|
||||
int topo_node_sqlite_get_public_peers(sqlite3* db, const char* channel_id,
|
||||
uint64_t** out_ids, int* out_count, uint64_t self_node_id) {
|
||||
if (!db || !channel_id || !out_ids || !out_count) return -1;
|
||||
*out_ids = NULL; *out_count = 0;
|
||||
char peers_tbl[80]; peers_table_name(channel_id, peers_tbl, sizeof(peers_tbl));
|
||||
char sql[384]; snprintf(sql, sizeof(sql),
|
||||
uint64_t** out_ids, int* out_count, uint64_t self_node_id) {
|
||||
if (out_ids) *out_ids = NULL;
|
||||
if (out_count) *out_count = 0;
|
||||
if (!db || !channel_id || !out_ids || !out_count) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "public peers: invalid arguments"); return -1;
|
||||
}
|
||||
char table[80], sql[512]; peers_table_name(channel_id, table, sizeof(table));
|
||||
snprintf(sql, sizeof(sql),
|
||||
"SELECT DISTINCT na.node_id FROM node_addresses na"
|
||||
" WHERE na.node_id!=?1 AND na.family IN (4,6) AND na.addr_type IN (1,2)"
|
||||
" AND EXISTS (SELECT 1 FROM nodes n WHERE n.node_id=na.node_id)"
|
||||
" AND NOT EXISTS (SELECT 1 FROM \"%s\" p WHERE p.node_id=na.node_id AND p.deleted=1)",
|
||||
peers_tbl);
|
||||
sqlite3_stmt* st = NULL;
|
||||
if (sqlite3_prepare_v2(db, sql, -1, &st, NULL) != SQLITE_OK) return -1;
|
||||
sqlite3_bind_int64(st, 1, (sqlite3_int64)self_node_id);
|
||||
int cnt = 0;
|
||||
while (sqlite3_step(st) == SQLITE_ROW) cnt++;
|
||||
if (cnt == 0) { sqlite3_finalize(st); return 0; }
|
||||
uint64_t* ids = u_malloc((size_t)cnt * sizeof(uint64_t));
|
||||
if (!ids) { sqlite3_finalize(st); return -1; }
|
||||
sqlite3_reset(st); int i = 0;
|
||||
while (sqlite3_step(st) == SQLITE_ROW) ids[i++] = (uint64_t)sqlite3_column_int64(st, 0);
|
||||
sqlite3_finalize(st);
|
||||
*out_ids = ids; *out_count = cnt;
|
||||
return 0;
|
||||
" AND NOT EXISTS (SELECT 1 FROM \"%s\" p WHERE p.node_id=na.node_id AND p.deleted=1)", table);
|
||||
return node_ids_query(db, sql, 1, self_node_id, out_ids, out_count);
|
||||
}
|
||||
|
||||
int topo_node_sqlite_get_supernode_peers(sqlite3* db, const char* channel_id,
|
||||
uint64_t** out_ids, int* out_count) {
|
||||
if (!db || !channel_id || !out_ids || !out_count) return -1;
|
||||
*out_ids = NULL; *out_count = 0;
|
||||
char peers_tbl[80]; peers_table_name(channel_id, peers_tbl, sizeof(peers_tbl));
|
||||
char sql[256]; snprintf(sql, sizeof(sql),
|
||||
"SELECT p.node_id FROM \"%s\" p WHERE p.node_type=4 AND p.deleted=0",
|
||||
peers_tbl);
|
||||
sqlite3_stmt* st = NULL;
|
||||
if (sqlite3_prepare_v2(db, sql, -1, &st, NULL) != SQLITE_OK) return -1;
|
||||
int cnt = 0;
|
||||
while (sqlite3_step(st) == SQLITE_ROW) cnt++;
|
||||
if (cnt == 0) { sqlite3_finalize(st); return 0; }
|
||||
uint64_t* ids = u_malloc((size_t)cnt * sizeof(uint64_t));
|
||||
if (!ids) { sqlite3_finalize(st); return -1; }
|
||||
sqlite3_reset(st); int i = 0;
|
||||
while (sqlite3_step(st) == SQLITE_ROW) ids[i++] = (uint64_t)sqlite3_column_int64(st, 0);
|
||||
sqlite3_finalize(st);
|
||||
*out_ids = ids; *out_count = cnt;
|
||||
return 0;
|
||||
int topo_node_sqlite_get_supernode_peers(sqlite3* db, const char* channel_id, uint64_t** out_ids, int* out_count) {
|
||||
if (out_ids) *out_ids = NULL;
|
||||
if (out_count) *out_count = 0;
|
||||
if (!db || !channel_id || !out_ids || !out_count) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "supernode peers: invalid arguments"); return -1;
|
||||
}
|
||||
char table[80], sql[192]; peers_table_name(channel_id, table, sizeof(table));
|
||||
snprintf(sql, sizeof(sql), "SELECT node_id FROM \"%s\" WHERE node_type=4 AND deleted=0", table);
|
||||
return node_ids_query(db, sql, 0, 0, out_ids, out_count);
|
||||
}
|
||||
|
||||
int topo_node_sqlite_get_local_peers(sqlite3* db, const char* channel_id,
|
||||
uint64_t** out_ids, int* out_count, uint64_t self_node_id) {
|
||||
if (!db || !channel_id || !out_ids || !out_count) return -1;
|
||||
*out_ids = NULL; *out_count = 0;
|
||||
char peers_tbl[80]; peers_table_name(channel_id, peers_tbl, sizeof(peers_tbl));
|
||||
char sql[384]; snprintf(sql, sizeof(sql),
|
||||
uint64_t** out_ids, int* out_count, uint64_t self_node_id) {
|
||||
if (out_ids) *out_ids = NULL;
|
||||
if (out_count) *out_count = 0;
|
||||
if (!db || !channel_id || !out_ids || !out_count) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "local peers: invalid arguments"); return -1;
|
||||
}
|
||||
char table[80], sql[512]; peers_table_name(channel_id, table, sizeof(table));
|
||||
snprintf(sql, sizeof(sql),
|
||||
"SELECT DISTINCT na.node_id FROM node_addresses na"
|
||||
" WHERE na.node_id!=?1 AND na.family IN (4,6) AND na.addr_type IN (0,3)"
|
||||
" AND EXISTS (SELECT 1 FROM nodes n WHERE n.node_id=na.node_id)"
|
||||
" AND NOT EXISTS (SELECT 1 FROM \"%s\" p WHERE p.node_id=na.node_id AND p.deleted=1)",
|
||||
peers_tbl);
|
||||
sqlite3_stmt* st = NULL;
|
||||
if (sqlite3_prepare_v2(db, sql, -1, &st, NULL) != SQLITE_OK) return -1;
|
||||
sqlite3_bind_int64(st, 1, (sqlite3_int64)self_node_id);
|
||||
int cnt = 0;
|
||||
while (sqlite3_step(st) == SQLITE_ROW) cnt++;
|
||||
if (cnt == 0) { sqlite3_finalize(st); return 0; }
|
||||
uint64_t* ids = u_malloc((size_t)cnt * sizeof(uint64_t));
|
||||
if (!ids) { sqlite3_finalize(st); return -1; }
|
||||
sqlite3_reset(st); int i = 0;
|
||||
while (sqlite3_step(st) == SQLITE_ROW) ids[i++] = (uint64_t)sqlite3_column_int64(st, 0);
|
||||
sqlite3_finalize(st);
|
||||
*out_ids = ids; *out_count = cnt;
|
||||
return 0;
|
||||
" AND NOT EXISTS (SELECT 1 FROM \"%s\" p WHERE p.node_id=na.node_id AND p.deleted=1)", table);
|
||||
return node_ids_query(db, sql, 1, self_node_id, out_ids, out_count);
|
||||
}
|
||||
|
||||
int topo_node_sqlite_get_node_type(sqlite3* db, const char* channel_id, uint64_t node_id) {
|
||||
|
||||
@@ -64,6 +64,8 @@ static void test_zero_port_schema(void) {
|
||||
}
|
||||
|
||||
/* Отказ INSERT/UPDATE не должен выглядеть успешным сохранением уже проверенного узла. */
|
||||
static int interrupt_sql(void* arg) { (void)arg; return 1; }
|
||||
|
||||
static void test_verified_write(void) {
|
||||
sqlite3* db = NULL; uint8_t key[32] = {1};
|
||||
assert(sqlite3_open(":memory:", &db) == SQLITE_OK && !topo_node_sqlite_init(db));
|
||||
@@ -89,6 +91,19 @@ static void test_verified_write(void) {
|
||||
assert(!topo_node_sqlite_member_placeholder_put(db, "123", 7, key, key));
|
||||
assert(!topo_node_sqlite_channel_put(db, "124", "test", 7, key, NULL, key, NULL, signature));
|
||||
assert(!topo_node_sqlite_member_placeholder_put(db, "124", 7, key, key));
|
||||
assert(!topo_node_sqlite_set_connected(db, "123", 7, 1));
|
||||
uint64_t* ids = NULL; int count = 0;
|
||||
assert(!topo_node_sqlite_get_connected_peers(db, "123", &ids, &count) && count == 1 && ids[0] == 7); u_free(ids);
|
||||
for (int query = 0; query < 4; query++) {
|
||||
ids = (void*)(uintptr_t)1; count = 99;
|
||||
sqlite3_progress_handler(db, 1, interrupt_sql, NULL);
|
||||
int rc = query == 0 ? topo_node_sqlite_get_connected_peers(db, "123", &ids, &count) :
|
||||
query == 1 ? topo_node_sqlite_get_supernode_peers(db, "123", &ids, &count) :
|
||||
query == 2 ? topo_node_sqlite_get_public_peers(db, "123", &ids, &count, 0) :
|
||||
topo_node_sqlite_get_local_peers(db, "123", &ids, &count, 0);
|
||||
sqlite3_progress_handler(db, 0, NULL, NULL);
|
||||
assert(rc < 0 && !ids && !count && !sqlite3_next_stmt(db, NULL));
|
||||
}
|
||||
assert(sqlite3_exec(db, "UPDATE peers_123 SET node_type=2; UPDATE peers_124 SET node_type=4;"
|
||||
"CREATE TRIGGER reject_classification BEFORE UPDATE ON peers_124 "
|
||||
"BEGIN SELECT RAISE(ABORT,'test classification rejection'); END", NULL, NULL, NULL) == SQLITE_OK);
|
||||
|
||||
Reference in new issue
Block a user