#pragma once #include #include #include #include /// @struct Country /// @brief Represents a country with geographic identifiers struct Country { int id; std::string name; std::string iso2; ///< 2-letter ISO code (e.g., "US", "CA") std::string iso3; ///< 3-letter ISO code (e.g., "USA", "CAN") }; /// @struct State /// @brief Represents a state or province with geographic identifiers struct State { int id; std::string name; std::string iso2; ///< 2-letter state code (e.g., "CA", "ON") int countryId; }; /** * @class SqliteDatabase * @brief Thread-safe in-memory SQLite database wrapper for geographic data * * Manages a local in-memory SQLite database with countries, states, and cities. * All write operations are serialized via mutex to enable safe concurrent * access from multiple threads. Uses INSERT OR IGNORE for idempotent * operations. * * Schema Relationships: * countries (id, name, iso2, iso3) * ↓ (one-to-many) * states (id, country_id, name, iso2) * ↓ (one-to-many) * cities (id, state_id, country_id, name, latitude, longitude) */ class SqliteDatabase { private: sqlite3 *db = nullptr; ///< SQLite database connection handle std::mutex dbMutex; ///< Protects all database operations from race conditions /// @brief Creates the schema with three related tables and foreign keys void InitializeSchema(); public: /// @brief Destructor: safely closes the database connection ~SqliteDatabase(); /// @brief Opens an in-memory SQLite database and initializes the schema void Initialize(); /// @brief Inserts a country record /// @param id Unique country identifier /// @param name Country name /// @param iso2 2-letter ISO country code /// @param iso3 3-letter ISO country code /// @note Thread-safe: uses mutex lock. Idempotent: INSERT OR IGNORE prevents /// duplicates void InsertCountry(int id, const std::string &name, const std::string &iso2, const std::string &iso3); /// @brief Inserts a state/province record /// @param id Unique state identifier /// @param countryId Foreign key reference to parent country /// @param name State/province name /// @param iso2 2-letter state code (e.g., "CA", "ON") /// @note Thread-safe and idempotent via mutex and INSERT OR IGNORE void InsertState(int id, int countryId, const std::string &name, const std::string &iso2); /// @brief Inserts a city record with geographic coordinates /// @param id Unique city identifier /// @param stateId Foreign key reference to parent state /// @param countryId Foreign key reference to parent country /// @param name City name /// @param latitude Geographic latitude coordinate (WGS84) /// @param longitude Geographic longitude coordinate (WGS84) /// @note Thread-safe and idempotent. Called by multithreaded JSON loader. void InsertCity(int id, int stateId, int countryId, const std::string &name, double latitude, double longitude); /// @brief Queries all cities from the database /// @return Vector of (city_id, city_name) pairs sorted alphabetically std::vector> QueryCities(); /// @brief Queries all countries from the database with ISO codes /// @param limit Maximum number of records to return (0 = all) /// @return Vector of Country structs (includes id, name, iso2, iso3) sorted /// alphabetically std::vector QueryCountries(int limit = 0); /// @brief Queries all states from the database with ISO codes /// @param limit Maximum number of records to return (0 = all) /// @return Vector of State structs (includes id, name, iso2, countryId) /// sorted alphabetically std::vector QueryStates(int limit = 0); };