chore: sync from fn-registry agent
This commit is contained in:
@@ -0,0 +1,110 @@
|
||||
#include "data_registry.h"
|
||||
|
||||
#include <sqlite3.h>
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
|
||||
namespace {
|
||||
|
||||
std::string col_text(sqlite3_stmt* st, int i) {
|
||||
const unsigned char* t = sqlite3_column_text(st, i);
|
||||
return t ? std::string(reinterpret_cast<const char*>(t)) : std::string();
|
||||
}
|
||||
|
||||
bool fill_row(sqlite3_stmt* st, RegistryRow& row) {
|
||||
row.id = col_text(st, 0);
|
||||
row.name = col_text(st, 1);
|
||||
row.kind = col_text(st, 2);
|
||||
row.lang = col_text(st, 3);
|
||||
row.domain = col_text(st, 4);
|
||||
row.purity = col_text(st, 5);
|
||||
row.signature = col_text(st, 6);
|
||||
row.description = col_text(st, 7);
|
||||
return true;
|
||||
}
|
||||
|
||||
// FTS5 escape: el caller pasa texto libre; lo encerramos en comillas dobles
|
||||
// y duplicamos las internas. No interpretamos operadores FTS5.
|
||||
std::string fts5_quote(const std::string& q) {
|
||||
std::string out;
|
||||
out.reserve(q.size() + 4);
|
||||
out.push_back('"');
|
||||
for (char c : q) {
|
||||
if (c == '"') out += "\"\"";
|
||||
else out.push_back(c);
|
||||
}
|
||||
out.push_back('"');
|
||||
return out;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool registry_open(OdrRegistry& r, const std::string& db_path) {
|
||||
if (r.db) sqlite3_close(r.db);
|
||||
r.db = nullptr;
|
||||
int rc = sqlite3_open_v2(db_path.c_str(), &r.db, SQLITE_OPEN_READONLY,
|
||||
nullptr);
|
||||
if (rc != SQLITE_OK) {
|
||||
std::fprintf(stderr, "[odr_console] sqlite_open: %s\n",
|
||||
sqlite3_errmsg(r.db));
|
||||
if (r.db) { sqlite3_close(r.db); r.db = nullptr; }
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void registry_close(OdrRegistry& r) {
|
||||
if (r.db) sqlite3_close(r.db);
|
||||
r.db = nullptr;
|
||||
}
|
||||
|
||||
bool registry_list_recent(OdrRegistry& r, int limit,
|
||||
std::vector<RegistryRow>& out) {
|
||||
out.clear();
|
||||
if (!r.db) return false;
|
||||
const char* sql =
|
||||
"SELECT id, name, kind, lang, domain, purity, signature, description "
|
||||
"FROM functions ORDER BY updated_at DESC LIMIT ?;";
|
||||
sqlite3_stmt* st = nullptr;
|
||||
if (sqlite3_prepare_v2(r.db, sql, -1, &st, nullptr) != SQLITE_OK) {
|
||||
return false;
|
||||
}
|
||||
sqlite3_bind_int(st, 1, limit);
|
||||
while (sqlite3_step(st) == SQLITE_ROW) {
|
||||
RegistryRow row;
|
||||
fill_row(st, row);
|
||||
out.push_back(std::move(row));
|
||||
}
|
||||
sqlite3_finalize(st);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool registry_search(OdrRegistry& r, const std::string& query, int limit,
|
||||
std::vector<RegistryRow>& out) {
|
||||
out.clear();
|
||||
if (!r.db) return false;
|
||||
|
||||
std::string match = fts5_quote(query);
|
||||
const char* sql =
|
||||
"SELECT f.id, f.name, f.kind, f.lang, f.domain, f.purity, "
|
||||
" f.signature, f.description "
|
||||
"FROM functions f "
|
||||
"WHERE f.id IN (SELECT id FROM functions_fts WHERE functions_fts MATCH ?) "
|
||||
"ORDER BY f.name LIMIT ?;";
|
||||
sqlite3_stmt* st = nullptr;
|
||||
if (sqlite3_prepare_v2(r.db, sql, -1, &st, nullptr) != SQLITE_OK) {
|
||||
std::fprintf(stderr, "[odr_console] prepare_v2 search: %s\n",
|
||||
sqlite3_errmsg(r.db));
|
||||
return false;
|
||||
}
|
||||
sqlite3_bind_text(st, 1, match.c_str(), -1, SQLITE_TRANSIENT);
|
||||
sqlite3_bind_int(st, 2, limit);
|
||||
while (sqlite3_step(st) == SQLITE_ROW) {
|
||||
RegistryRow row;
|
||||
fill_row(st, row);
|
||||
out.push_back(std::move(row));
|
||||
}
|
||||
sqlite3_finalize(st);
|
||||
return true;
|
||||
}
|
||||
Reference in New Issue
Block a user