#include "data_registry.h" #include #include #include 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(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& 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& 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; }