Add user and brewery address tables

This commit is contained in:
Aaron Po
2026-07-13 19:59:18 -04:00
parent d52dba904c
commit 58e476afdb
13 changed files with 237 additions and 151 deletions

View File

@@ -7,6 +7,7 @@
* enriched data, and complete generation results.
*/
#include <optional>
#include <string>
#include "data_model/models.h"
@@ -92,22 +93,40 @@ struct EnrichedCity {
};
/**
* @brief A brewery's street-level address, distinct from the shared `City`
* it belongs to. Mirrors the backend's `BreweryPostLocation` shape.
* @brief A brewery's address. Owns the `City` it belongs to alongside its
* street-level fields. Mirrors the `brewery_addresses` table and the
* backend's `BreweryPostLocation` shape.
*
* Only `postal_code` is populated today -- street-address generation has no
* fixture data or service yet.
* Only `city` and `postal_code` are populated today -- street-address
* generation has no fixture data or service yet, so the address lines stay
* empty.
*/
struct Address {
struct BreweryAddress {
City city{};
std::optional<std::string> address_line_1{};
std::optional<std::string> address_line_2{};
std::string postal_code{};
};
/**
* @brief A user's address. Owns the `City` it belongs to alongside its
* street-level fields. Mirrors the `user_addresses` table.
*
* Only `city` is populated today; the street-level columns exist for future
* enrichment.
*/
struct UserAddress {
City city{};
std::optional<std::string> address_line_1{};
std::optional<std::string> address_line_2{};
std::optional<std::string> postal_code{};
};
/**
* @brief Helper struct to store generated brewery data.
*/
struct BreweryRecord {
City location;
Address address;
BreweryAddress address;
BreweryResult brewery;
};
@@ -119,7 +138,7 @@ struct BreweryRecord {
* consumer can register real accounts from the pipeline's SQLite export.
*/
struct UserRecord {
City location;
UserAddress address{};
UserResult user;
std::string email{};
std::string date_of_birth{};

View File

@@ -60,16 +60,6 @@ struct City {
* @brief Local language codes in priority order.
*/
std::vector<std::string> local_languages{};
/**
* @brief Latitude in decimal degrees.
*/
double latitude{};
/**
* @brief Longitude in decimal degrees.
*/
double longitude{};
};
// ============================================================================

View File

@@ -63,7 +63,9 @@ class SqliteExportService final : public IExportService {
SqliteDatabaseHandle db_handle_;
SqliteStatementHandle insert_city_stmt_;
SqliteStatementHandle insert_brewery_stmt_;
SqliteStatementHandle insert_brewery_address_stmt_;
SqliteStatementHandle insert_user_stmt_;
SqliteStatementHandle insert_user_address_stmt_;
bool transaction_open_ = false;
std::unordered_map<std::string, sqlite3_int64> city_cache_;
};

View File

@@ -27,9 +27,7 @@ CREATE TABLE IF NOT EXISTS cities (
country TEXT NOT NULL,
iso3166_1 TEXT NOT NULL,
local_languages_json TEXT NOT NULL,
latitude REAL NOT NULL,
longitude REAL NOT NULL,
UNIQUE(city, state_province, iso3166_2, country, latitude, longitude)
UNIQUE(city, state_province, iso3166_2, country)
);
)sql";
@@ -43,19 +41,16 @@ CREATE TABLE IF NOT EXISTS breweries (
description_en TEXT NOT NULL,
name_local TEXT NOT NULL,
description_local TEXT NOT NULL,
postal_code TEXT NOT NULL,
FOREIGN KEY(city_id) REFERENCES cities(id) ON DELETE CASCADE
);
CREATE INDEX IF NOT EXISTS idx_breweries_city_id ON breweries(city_id);
)sql";
inline constexpr std::string_view kCreateUsersTableSql = R"sql(
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
city_id INTEGER NOT NULL,
first_name TEXT NOT NULL,
last_name TEXT NOT NULL,
gender TEXT NOT NULL,
@@ -63,12 +58,45 @@ CREATE TABLE IF NOT EXISTS users (
bio TEXT NOT NULL,
activity_weight REAL NOT NULL,
email TEXT NOT NULL UNIQUE,
date_of_birth TEXT NOT NULL,
date_of_birth TEXT NOT NULL
);
)sql";
inline constexpr std::string_view kCreateBreweryAddressTableSql = R"sql(
CREATE TABLE IF NOT EXISTS brewery_addresses (
id INTEGER PRIMARY KEY AUTOINCREMENT,
brewery_id INTEGER NOT NULL,
address_line_1 TEXT,
address_line_2 TEXT,
postal_code TEXT NOT NULL,
city_id INTEGER NOT NULL,
FOREIGN KEY(brewery_id) REFERENCES breweries(id) ON DELETE CASCADE,
FOREIGN KEY(city_id) REFERENCES cities(id) ON DELETE CASCADE
);
CREATE INDEX IF NOT EXISTS idx_users_city_id ON users(city_id);
CREATE INDEX IF NOT EXISTS idx_brewery_addresses_brewery_id
ON brewery_addresses(brewery_id);
CREATE INDEX IF NOT EXISTS idx_brewery_addresses_city_id
ON brewery_addresses(city_id);
)sql";
inline constexpr std::string_view kCreateUserAddressTableSql = R"sql(
CREATE TABLE IF NOT EXISTS user_addresses (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER NOT NULL,
address_line_1 TEXT,
address_line_2 TEXT,
postal_code TEXT,
city_id INTEGER NOT NULL,
FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE,
FOREIGN KEY(city_id) REFERENCES cities(id) ON DELETE CASCADE
);
CREATE INDEX IF NOT EXISTS idx_user_addresses_user_id
ON user_addresses(user_id);
CREATE INDEX IF NOT EXISTS idx_user_addresses_city_id
ON user_addresses(city_id);
)sql";
inline constexpr std::string_view kInsertCitySql = R"sql(
@@ -78,10 +106,8 @@ INSERT INTO cities (
iso3166_2,
country,
iso3166_1,
local_languages_json,
latitude,
longitude
) VALUES (?, ?, ?, ?, ?, ?, ?, ?);
local_languages_json
) VALUES (?, ?, ?, ?, ?, ?);
)sql";
inline constexpr std::string_view kInsertBrewerySql = R"sql(
@@ -90,14 +116,12 @@ INSERT INTO breweries (
name_en,
description_en,
name_local,
description_local,
postal_code
) VALUES (?, ?, ?, ?, ?, ?);
description_local
) VALUES (?, ?, ?, ?, ?);
)sql";
inline constexpr std::string_view kInsertUserSql = R"sql(
INSERT INTO users (
city_id,
first_name,
last_name,
gender,
@@ -106,7 +130,22 @@ INSERT INTO users (
activity_weight,
email,
date_of_birth
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?);
) VALUES (?, ?, ?, ?, ?, ?, ?, ?);
)sql";
inline constexpr std::string_view kInsertBreweryAddressSql = R"sql(
INSERT INTO brewery_addresses (
brewery_id,
postal_code,
city_id
) VALUES (?, ?, ?);
)sql";
inline constexpr std::string_view kInsertUserAddressSql = R"sql(
INSERT INTO user_addresses (
user_id,
city_id
) VALUES (?, ?);
)sql";
// sqlite3_bind_*() parameter indices are 1-based, matching the "?"
@@ -118,8 +157,6 @@ enum CityBindIndex {
kCityCountryBindIndex,
kCityIso31661BindIndex,
kCityLanguagesBindIndex,
kCityLatitudeBindIndex,
kCityLongitudeBindIndex,
};
enum BreweryBindIndex {
@@ -128,12 +165,10 @@ enum BreweryBindIndex {
kBreweryEnglishDescriptionBindIndex,
kBreweryLocalNameBindIndex,
kBreweryLocalDescriptionBindIndex,
kBreweryPostalCodeBindIndex,
};
enum UserBindIndex {
kUserCityIdBindIndex = 1,
kUserFirstNameBindIndex,
kUserFirstNameBindIndex = 1,
kUserLastNameBindIndex,
kUserGenderBindIndex,
kUserUsernameBindIndex,
@@ -143,6 +178,17 @@ enum UserBindIndex {
kUserDateOfBirthBindIndex,
};
enum BreweryAddressBindIndex {
kBreweryAddressBreweryIdBindIndex = 1,
kBreweryAddressPostalCodeBindIndex,
kBreweryAddressCityIdBindIndex,
};
enum UserAddressBindIndex {
kUserAddressUserIdBindIndex = 1,
kUserAddressCityIdBindIndex,
};
SqliteStatementHandle PrepareStatement(const SqliteDatabaseHandle& db_handle,
std::string_view sql,
const char* action);