#pragma once #include #include #include #include #include #include using namespace std::string_literals; std::set get_all_columns_from_table(sqlite3* db, const std::string& table_name); template void add_column_to_table(sqlite3* db, const std::string& table_name) { const std::string name = ColumnType::name; std::string query{"ALTER TABLE " + table_name + " ADD " + ColumnType::name + " " + TypeToSQLType::type}; char* error; const auto result = sqlite3_exec(db, query.data(), nullptr, nullptr, &error); if (result != SQLITE_OK) { log_error("Error adding column ", name, " to table ", table_name, ": ", error); sqlite3_free(error); } } template void append_option(std::string& s) { s += " "s + ColumnType::options; } template void append_option(Args&& ...) { } template class Table { static_assert(sizeof...(T) > 0, "Table cannot be empty"); using ColumnTypes = std::tuple; public: using RowType = Row; Table(std::string name): name(std::move(name)) {} void upgrade(sqlite3* db) { const auto existing_columns = get_all_columns_from_table(db, this->name); add_column_if_not_exists(db, existing_columns); } void create(sqlite3* db) { std::string res{"CREATE TABLE IF NOT EXISTS "}; res += this->name; res += " (\n"; this->add_column_create(res); res += ")"; char* error; const auto result = sqlite3_exec(db, res.data(), nullptr, nullptr, &error); if (result != SQLITE_OK) { log_error("Error executing query: ", error); sqlite3_free(error); } } RowType row() { return {this->name}; } SelectQuery select() { SelectQuery select(this->name); return select; } const std::string& get_name() const { return this->name; } private: template typename std::enable_if::type add_column_if_not_exists(sqlite3* db, const std::set& existing_columns) { using ColumnType = typename std::remove_reference(std::declval()))>::type; if (existing_columns.count(ColumnType::name) != 1) { add_column_to_table(db, this->name); } add_column_if_not_exists(db, existing_columns); } template typename std::enable_if::type add_column_if_not_exists(sqlite3*, const std::set&) {} template typename std::enable_if::type add_column_create(std::string& str) { using ColumnType = typename std::remove_reference(std::declval()))>::type; using RealType = typename ColumnType::real_type; str += ColumnType::name; str += " "; str += TypeToSQLType::type; append_option(str); if (N != sizeof...(T) - 1) str += ","; str += "\n"; add_column_create(str); } template typename std::enable_if::type add_column_create(std::string&) { } const std::string name; };