#include #include #include #include #include #include #include #include "data.hpp" // id, owner_id, parent_id, name, url, desc, visibility, time. #define LINK_TUPLE_TYPES int64_t,int64_t,int64_t,std::string,std::string,std::string,int,int64_t namespace { using LinkTuple = std::tuple; LinkItem linkFromTuple(const LinkTuple& t) { LinkItem l; l.id = std::get<0>(t); l.owner_id = std::get<1>(t); int64_t p = std::get<2>(t); if(p == 0) { l.parent_id = std::nullopt; } else { l.parent_id = p; } l.name = std::get<3>(t); l.url = std::get<4>(t); l.description = std::get<5>(t); l.visibility = static_cast(std::get<6>(t)); l.time = mw::secondsToTime(std::get<7>(t)); return l; } } // namespace mw::E> DataSourceSQLite::fromFile(const std::string& db_file) { auto data_source = std::make_unique(); ASSIGN_OR_RETURN(data_source->db, mw::SQLite::connectFile(db_file)); DO_OR_RETURN(data_source->db->execute("PRAGMA foreign_keys = ON;")); // Perform schema upgrade here. // // data_source->upgradeSchema1To2(); // Update this line when schema updates. DO_OR_RETURN(data_source->setSchemaVersion(1)); DO_OR_RETURN(data_source->db->execute( "CREATE TABLE IF NOT EXISTS Users " "(id INTEGER PRIMARY KEY, openid_uid TEXT, name TEXT);")); DO_OR_RETURN(data_source->db->execute( "CREATE TABLE IF NOT EXISTS LinkItems " "(id INTEGER PRIMARY KEY, owner_id INTEGER NOT NULL," " parent_id INTEGER," " name TEXT NOT NULL, url TEXT, description TEXT," " visibility INTEGER NOT NULL, time INTEGER NOT NULL," " FOREIGN KEY(owner_id) REFERENCES Users(id)," " FOREIGN KEY(parent_id) REFERENCES LinkItems(id));")); return data_source; } mw::E> DataSourceSQLite::newFromMemory() { return fromFile(":memory:"); } mw::E DataSourceSQLite::getSchemaVersion() const { return db->evalToValue("PRAGMA user_version;"); } mw::E DataSourceSQLite::setSchemaVersion(int64_t v) const { return db->execute(std::format("PRAGMA user_version = {};", v)); } mw::E> DataSourceSQLite::items(std::optional parent) { std::vector links; if(!parent.has_value()) { // Querying root-level links. ASSIGN_OR_RETURN(links, (db->eval( "SELECT id, owner_id, parent_id, name, url, description, visibility," " time from LinkItems WHERE parent_id IS NULL;"))); } else { ASSIGN_OR_RETURN(mw::SQLiteStatement stat, db->statementFromStr( "SELECT id, owner_id, parent_id, name, url, description, visibility," " time from LinkItems WHERE parent_id = ?;")); DO_OR_RETURN(stat.bind(*parent)); ASSIGN_OR_RETURN(links, (db->eval(std::move(stat)))); } std::vector result; for(const LinkTuple& t : links) { result.push_back(linkFromTuple(t)); } return result; } mw::E DataSourceSQLite::addLink(LinkItem&& link) { ASSIGN_OR_RETURN(mw::SQLiteStatement sql, db->statementFromStr( "INSERT INTO LinkItems (id, owner_id, parent_id, name, url," " description, visibility, time) VALUES (?, ?, ?, ?, ?, ?, ?, ?)")); if(link.parent_id.has_value()) { DO_OR_RETURN(sql.bind( std::nullopt, link.owner_id, *link.parent_id, link.name, link.url, link.description, static_cast(link.visibility), mw::timeToSeconds(mw::Clock::now()))); } else { DO_OR_RETURN(sql.bind( std::nullopt, link.owner_id, std::nullopt, link.name, link.url, link.description, static_cast(link.visibility), mw::timeToSeconds(mw::Clock::now()))); } DO_OR_RETURN(db->execute(std::move(sql))); return db->lastInsertRowID(); }