mirror of
https://github.com/aaronpo97/the-biergarten-app.git
synced 2026-07-16 17:47:22 +00:00
Add user and brewery address tables
This commit is contained in:
@@ -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{};
|
||||
|
||||
@@ -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{};
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
|
||||
@@ -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_;
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -29,9 +29,10 @@ void BiergartenPipelineOrchestrator::GenerateBreweries(
|
||||
generator_->GenerateBrewery(location, region_context);
|
||||
const std::string postal_code =
|
||||
postal_code_service_->GeneratePostalCode(location);
|
||||
return BreweryRecord{.location = location,
|
||||
.address = Address{.postal_code = postal_code},
|
||||
.brewery = brewery};
|
||||
return BreweryRecord{
|
||||
.address =
|
||||
BreweryAddress{.city = location, .postal_code = postal_code},
|
||||
.brewery = brewery};
|
||||
} catch (const std::exception& e) {
|
||||
++skipped_count;
|
||||
|
||||
@@ -51,13 +52,13 @@ void BiergartenPipelineOrchestrator::GenerateBreweries(
|
||||
exporter_->ProcessRecord(record);
|
||||
} catch (const std::exception& export_exception) {
|
||||
++export_failed_count;
|
||||
logger_->Log(
|
||||
{.level = LogLevel::Warn,
|
||||
.phase = PipelinePhase::BreweryAndBeerGeneration,
|
||||
.message = std::format("[Pipeline] Generated brewery for '{}' ({}) "
|
||||
"but SQLite export failed: {}",
|
||||
record.location.city, record.location.country,
|
||||
export_exception.what())});
|
||||
logger_->Log({.level = LogLevel::Warn,
|
||||
.phase = PipelinePhase::BreweryAndBeerGeneration,
|
||||
.message = std::format(
|
||||
"[Pipeline] Generated brewery for '{}' ({}) "
|
||||
"but SQLite export failed: {}",
|
||||
record.address.city.city, record.address.city.country,
|
||||
export_exception.what())});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -137,7 +137,7 @@ void BiergartenPipelineOrchestrator::GenerateUsers(
|
||||
generator_->GenerateUser(city, persona, sampled_name);
|
||||
|
||||
return UserRecord{
|
||||
.location = city.location,
|
||||
.address = UserAddress{.city = city.location},
|
||||
.user = user,
|
||||
.email = BuildEmail(sampled_name, used_email_local_parts),
|
||||
.date_of_birth = GenerateDateOfBirth(rng),
|
||||
@@ -161,13 +161,13 @@ void BiergartenPipelineOrchestrator::GenerateUsers(
|
||||
} catch (const std::exception& export_exception) {
|
||||
++export_failed_count;
|
||||
|
||||
logger_->Log(
|
||||
{.level = LogLevel::Warn,
|
||||
.phase = PipelinePhase::UserGeneration,
|
||||
.message = std::format("[Pipeline] Generated user for '{}' ({}) but "
|
||||
"SQLite export failed: {}",
|
||||
record.location.city, record.location.country,
|
||||
export_exception.what())});
|
||||
logger_->Log({.level = LogLevel::Warn,
|
||||
.phase = PipelinePhase::UserGeneration,
|
||||
.message = std::format(
|
||||
"[Pipeline] Generated user for '{}' ({}) but "
|
||||
"SQLite export failed: {}",
|
||||
record.address.city.city, record.address.city.country,
|
||||
export_exception.what())});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -14,7 +14,8 @@
|
||||
void BiergartenPipelineOrchestrator::LogResults() const {
|
||||
boost::json::array brewery_output;
|
||||
|
||||
for (const auto& [location, address, brewery] : generated_breweries_) {
|
||||
for (const auto& [address, brewery] : generated_breweries_) {
|
||||
const City& location = address.city;
|
||||
brewery_output.push_back(boost::json::object{
|
||||
{"name_en", brewery.name_en},
|
||||
{"description_en", brewery.description_en},
|
||||
@@ -26,8 +27,6 @@ void BiergartenPipelineOrchestrator::LogResults() const {
|
||||
{"country", location.country},
|
||||
{"state_province", location.state_province},
|
||||
{"iso3166_2", location.iso3166_2},
|
||||
{"latitude", location.latitude},
|
||||
{"longitude", location.longitude},
|
||||
}}});
|
||||
}
|
||||
|
||||
|
||||
@@ -136,8 +136,7 @@ const CityList& CuratedJsonDataService::LoadCities() {
|
||||
|
||||
const boost::json::value* postal_code = object.if_contains("postal_code");
|
||||
if (postal_code == nullptr || !postal_code->is_object()) {
|
||||
throw std::runtime_error(
|
||||
"Missing or invalid object field: postal_code");
|
||||
throw std::runtime_error("Missing or invalid object field: postal_code");
|
||||
}
|
||||
|
||||
locations.push_back(City{
|
||||
@@ -147,8 +146,6 @@ const CityList& CuratedJsonDataService::LoadCities() {
|
||||
.country = ReadRequiredString(object, "country"),
|
||||
.iso3166_1 = ReadRequiredString(object, "iso3166_1"),
|
||||
.local_languages = ReadRequiredStringArray(object, "local_languages"),
|
||||
.latitude = ReadRequiredNumber(object, "latitude"),
|
||||
.longitude = ReadRequiredNumber(object, "longitude"),
|
||||
.postal_regex =
|
||||
ReadRequiredStringArray(postal_code->as_object(), "city_regex"),
|
||||
.postal_code_examples =
|
||||
@@ -159,13 +156,21 @@ const CityList& CuratedJsonDataService::LoadCities() {
|
||||
for (auto location : locations) {
|
||||
std::cout << "Location: " << location.city << ", "
|
||||
<< location.state_province << ", " << location.iso3166_2 << ", "
|
||||
<< location.country << ", " << location.iso3166_1 << ", "
|
||||
<< location.latitude << ", " << location.longitude << std::endl;
|
||||
<< location.country << ", " << location.iso3166_1 << "\n";
|
||||
|
||||
for (const auto& regex : location.postal_regex) {
|
||||
std::cout << " Postal regex: " << regex << std::endl;
|
||||
std::cout << " Postal regex: " << regex << "\n";
|
||||
}
|
||||
|
||||
for (const auto& example : location.postal_code_examples) {
|
||||
std::cout << " Postal example: " << example << "\n";
|
||||
}
|
||||
|
||||
for (const auto& lang : location.local_languages) {
|
||||
std::cout << " Local language: " << lang << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
cache_.locations = std::move(locations);
|
||||
return cache_.locations;
|
||||
}
|
||||
|
||||
@@ -7,46 +7,38 @@
|
||||
|
||||
MockCuratedDataService::MockCuratedDataService()
|
||||
: locations_{
|
||||
City{.city = "Portland",
|
||||
.state_province = "Oregon",
|
||||
.iso3166_2 = "US-OR",
|
||||
.country = "United States",
|
||||
.iso3166_1 = "US",
|
||||
.local_languages = {"en"},
|
||||
.latitude = 45.5152,
|
||||
.longitude = -122.6784,
|
||||
.postal_regex = {"^972[0-9]{2}(?:-[0-9]{4})?$"},
|
||||
.postal_code_examples = {"97201", "97294"}},
|
||||
City{.city = "Munich",
|
||||
.state_province = "Bavaria",
|
||||
.iso3166_2 = "DE-BY",
|
||||
.country = "Germany",
|
||||
.iso3166_1 = "DE",
|
||||
.local_languages = {"de"},
|
||||
.latitude = 48.1351,
|
||||
.longitude = 11.5820,
|
||||
.postal_regex = {"^8[01][0-9]{3}$"},
|
||||
.postal_code_examples = {"80331", "81929"}},
|
||||
City{.city = "Lyon",
|
||||
.state_province = "Auvergne-Rhone-Alpes",
|
||||
.iso3166_2 = "FR-ARA",
|
||||
.country = "France",
|
||||
.iso3166_1 = "FR",
|
||||
.local_languages = {"fr"},
|
||||
.latitude = 45.7640,
|
||||
.longitude = 4.8357,
|
||||
.postal_regex = {"^6900[1-9]$"},
|
||||
.postal_code_examples = {"69001", "69009"}},
|
||||
City{.city = "Brussels",
|
||||
.state_province = "Brussels-Capital",
|
||||
.iso3166_2 = "BE-BRU",
|
||||
.country = "Belgium",
|
||||
.iso3166_1 = "BE",
|
||||
.local_languages = {"nl", "fr"},
|
||||
.latitude = 50.8503,
|
||||
.longitude = 4.3517,
|
||||
.postal_regex = {"^1(?:0[0-9]{2}|1[0-9]{2}|20[0-9]|210)$"},
|
||||
.postal_code_examples = {"1000", "1210"}},
|
||||
{.city = "Portland",
|
||||
.state_province = "Oregon",
|
||||
.iso3166_2 = "US-OR",
|
||||
.country = "United States",
|
||||
.iso3166_1 = "US",
|
||||
.local_languages = {"en"},
|
||||
.postal_regex = {"^972[0-9]{2}(?:-[0-9]{4})?$"},
|
||||
.postal_code_examples = {"97201", "97294"}},
|
||||
{.city = "Munich",
|
||||
.state_province = "Bavaria",
|
||||
.iso3166_2 = "DE-BY",
|
||||
.country = "Germany",
|
||||
.iso3166_1 = "DE",
|
||||
.local_languages = {"de"},
|
||||
.postal_regex = {"^8[01][0-9]{3}$"},
|
||||
.postal_code_examples = {"80331", "81929"}},
|
||||
{.city = "Lyon",
|
||||
.state_province = "Auvergne-Rhone-Alpes",
|
||||
.iso3166_2 = "FR-ARA",
|
||||
.country = "France",
|
||||
.iso3166_1 = "FR",
|
||||
.local_languages = {"fr"},
|
||||
.postal_regex = {"^6900[1-9]$"},
|
||||
.postal_code_examples = {"69001", "69009"}},
|
||||
{.city = "Brussels",
|
||||
.state_province = "Brussels-Capital",
|
||||
.iso3166_2 = "BE-BRU",
|
||||
.country = "Belgium",
|
||||
.iso3166_1 = "BE",
|
||||
.local_languages = {"nl", "fr"},
|
||||
.postal_regex = {"^1(?:0[0-9]{2}|1[0-9]{2}|20[0-9]|210)$"},
|
||||
.postal_code_examples = {"1000", "1210"}},
|
||||
},
|
||||
personas_{
|
||||
UserPersona{.name = "Hophead Explorer",
|
||||
@@ -91,9 +83,7 @@ MockCuratedDataService::MockCuratedDataService()
|
||||
{"BE", SurnameList{"Peeters", "Janssens"}},
|
||||
} {}
|
||||
|
||||
const CityList& MockCuratedDataService::LoadCities() {
|
||||
return locations_;
|
||||
}
|
||||
const CityList& MockCuratedDataService::LoadCities() { return locations_; }
|
||||
|
||||
const PersonasList& MockCuratedDataService::LoadPersonas() { return personas_; }
|
||||
|
||||
|
||||
@@ -14,7 +14,9 @@ void SqliteExportService::Finalize() {
|
||||
}
|
||||
|
||||
try {
|
||||
insert_user_address_stmt_.reset();
|
||||
insert_user_stmt_.reset();
|
||||
insert_brewery_address_stmt_.reset();
|
||||
insert_brewery_stmt_.reset();
|
||||
insert_city_stmt_.reset();
|
||||
if (transaction_open_) {
|
||||
|
||||
@@ -36,6 +36,12 @@ void SqliteExportService::InitializeSchema() const {
|
||||
sqlite_export_service_internal::ExecSql(
|
||||
db_handle_, sqlite_export_service_internal::kCreateUsersTableSql,
|
||||
"Failed to create SQLite users table");
|
||||
sqlite_export_service_internal::ExecSql(
|
||||
db_handle_, sqlite_export_service_internal::kCreateBreweryAddressTableSql,
|
||||
"Failed to create SQLite brewery addresses table");
|
||||
sqlite_export_service_internal::ExecSql(
|
||||
db_handle_, sqlite_export_service_internal::kCreateUserAddressTableSql,
|
||||
"Failed to create SQLite user addresses table");
|
||||
}
|
||||
|
||||
void SqliteExportService::PrepareStatements() {
|
||||
@@ -45,9 +51,16 @@ void SqliteExportService::PrepareStatements() {
|
||||
insert_brewery_stmt_ = sqlite_export_service_internal::PrepareStatement(
|
||||
db_handle_, sqlite_export_service_internal::kInsertBrewerySql,
|
||||
"Failed to prepare SQLite brewery insert statement");
|
||||
insert_brewery_address_stmt_ =
|
||||
sqlite_export_service_internal::PrepareStatement(
|
||||
db_handle_, sqlite_export_service_internal::kInsertBreweryAddressSql,
|
||||
"Failed to prepare SQLite brewery address insert statement");
|
||||
insert_user_stmt_ = sqlite_export_service_internal::PrepareStatement(
|
||||
db_handle_, sqlite_export_service_internal::kInsertUserSql,
|
||||
"Failed to prepare SQLite user insert statement");
|
||||
insert_user_address_stmt_ = sqlite_export_service_internal::PrepareStatement(
|
||||
db_handle_, sqlite_export_service_internal::kInsertUserAddressSql,
|
||||
"Failed to prepare SQLite user address insert statement");
|
||||
}
|
||||
|
||||
void SqliteExportService::RollbackAndCloseNoThrow() noexcept {
|
||||
@@ -60,7 +73,9 @@ void SqliteExportService::RollbackAndCloseNoThrow() noexcept {
|
||||
transaction_open_ = false;
|
||||
}
|
||||
|
||||
insert_user_address_stmt_.reset();
|
||||
insert_user_stmt_.reset();
|
||||
insert_brewery_address_stmt_.reset();
|
||||
insert_brewery_stmt_.reset();
|
||||
insert_city_stmt_.reset();
|
||||
db_handle_.reset();
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
* and the shared city-resolution helper.
|
||||
*/
|
||||
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
@@ -12,8 +11,6 @@
|
||||
#include "services/database/sqlite_export_service.h"
|
||||
#include "services/database/sqlite_export_service_helpers.h"
|
||||
|
||||
constexpr int kLocationPrecision = 17;
|
||||
|
||||
std::string SqliteExportService::BuildCityKey(const City& location) {
|
||||
std::ostringstream key_stream;
|
||||
key_stream << location.city << '\n'
|
||||
@@ -21,10 +18,6 @@ std::string SqliteExportService::BuildCityKey(const City& location) {
|
||||
<< location.iso3166_2 << '\n'
|
||||
<< location.country << '\n'
|
||||
<< location.iso3166_1 << '\n'
|
||||
<< std::setprecision(kLocationPrecision) << location.latitude
|
||||
<< '\n'
|
||||
<< std::setprecision(kLocationPrecision) << location.longitude
|
||||
<< '\n'
|
||||
<< sqlite_export_service_internal::SerializeVector(
|
||||
location.local_languages);
|
||||
return key_stream.str();
|
||||
@@ -49,8 +42,7 @@ sqlite3_int64 SqliteExportService::ResolveCityId(const City& location) {
|
||||
sqlite_export_service_internal::Bind(
|
||||
insert_city_stmt_,
|
||||
sqlite_export_service_internal::BoundParam<std::string_view>{
|
||||
.index =
|
||||
sqlite_export_service_internal::kCityStateProvinceBindIndex,
|
||||
.index = sqlite_export_service_internal::kCityStateProvinceBindIndex,
|
||||
.value = location.state_province,
|
||||
.action = "Failed to bind SQLite city state/province"});
|
||||
sqlite_export_service_internal::Bind(
|
||||
@@ -77,22 +69,9 @@ sqlite3_int64 SqliteExportService::ResolveCityId(const City& location) {
|
||||
.index = sqlite_export_service_internal::kCityLanguagesBindIndex,
|
||||
.value = local_languages_json,
|
||||
.action = "Failed to bind SQLite city languages"});
|
||||
sqlite_export_service_internal::Bind(
|
||||
insert_city_stmt_,
|
||||
sqlite_export_service_internal::BoundParam{
|
||||
.index = sqlite_export_service_internal::kCityLatitudeBindIndex,
|
||||
.value = location.latitude,
|
||||
.action = "Failed to bind SQLite city latitude"});
|
||||
sqlite_export_service_internal::Bind(
|
||||
insert_city_stmt_,
|
||||
sqlite_export_service_internal::BoundParam{
|
||||
.index = sqlite_export_service_internal::kCityLongitudeBindIndex,
|
||||
.value = location.longitude,
|
||||
.action = "Failed to bind SQLite city longitude"});
|
||||
|
||||
sqlite_export_service_internal::StepStatement(
|
||||
db_handle_, insert_city_stmt_,
|
||||
"Failed to insert SQLite city row");
|
||||
db_handle_, insert_city_stmt_, "Failed to insert SQLite city row");
|
||||
|
||||
const sqlite3_int64 city_id =
|
||||
sqlite_export_service_internal::LastInsertRowId(db_handle_);
|
||||
@@ -107,7 +86,7 @@ uint64_t SqliteExportService::ProcessRecord(const BreweryRecord& brewery) {
|
||||
throw std::runtime_error("SQLite export service is not initialized");
|
||||
}
|
||||
|
||||
const sqlite3_int64 city_id = ResolveCityId(brewery.location);
|
||||
const sqlite3_int64 city_id = ResolveCityId(brewery.address.city);
|
||||
|
||||
sqlite_export_service_internal::Bind(
|
||||
insert_brewery_stmt_,
|
||||
@@ -146,17 +125,40 @@ uint64_t SqliteExportService::ProcessRecord(const BreweryRecord& brewery) {
|
||||
.value = brewery.brewery.description_local,
|
||||
.action = "Failed to bind SQLite brewery local description"});
|
||||
|
||||
sqlite_export_service_internal::Bind(
|
||||
insert_brewery_stmt_,
|
||||
sqlite_export_service_internal::BoundParam<std::string_view>{
|
||||
.index = sqlite_export_service_internal::kBreweryPostalCodeBindIndex,
|
||||
.value = brewery.address.postal_code,
|
||||
.action = "Failed to bind SQLite brewery postal code"});
|
||||
|
||||
sqlite_export_service_internal::StepStatement(
|
||||
db_handle_, insert_brewery_stmt_, "Failed to insert SQLite brewery row");
|
||||
|
||||
const sqlite3_int64 brewery_id =
|
||||
sqlite_export_service_internal::LastInsertRowId(db_handle_);
|
||||
sqlite_export_service_internal::ResetStatement(insert_brewery_stmt_);
|
||||
|
||||
return sqlite_export_service_internal::LastInsertRowId(db_handle_);
|
||||
sqlite_export_service_internal::Bind(
|
||||
insert_brewery_address_stmt_,
|
||||
sqlite_export_service_internal::BoundParam<sqlite3_int64>{
|
||||
.index =
|
||||
sqlite_export_service_internal::kBreweryAddressBreweryIdBindIndex,
|
||||
.value = brewery_id,
|
||||
.action = "Failed to bind SQLite brewery address brewery id"});
|
||||
sqlite_export_service_internal::Bind(
|
||||
insert_brewery_address_stmt_,
|
||||
sqlite_export_service_internal::BoundParam<std::string_view>{
|
||||
.index = sqlite_export_service_internal::
|
||||
kBreweryAddressPostalCodeBindIndex,
|
||||
.value = brewery.address.postal_code,
|
||||
.action = "Failed to bind SQLite brewery address postal code"});
|
||||
sqlite_export_service_internal::Bind(
|
||||
insert_brewery_address_stmt_,
|
||||
sqlite_export_service_internal::BoundParam<sqlite3_int64>{
|
||||
.index =
|
||||
sqlite_export_service_internal::kBreweryAddressCityIdBindIndex,
|
||||
.value = city_id,
|
||||
.action = "Failed to bind SQLite brewery address city id"});
|
||||
|
||||
sqlite_export_service_internal::StepStatement(
|
||||
db_handle_, insert_brewery_address_stmt_,
|
||||
"Failed to insert SQLite brewery address row");
|
||||
|
||||
sqlite_export_service_internal::ResetStatement(insert_brewery_address_stmt_);
|
||||
|
||||
return brewery_id;
|
||||
}
|
||||
|
||||
@@ -13,14 +13,8 @@ uint64_t SqliteExportService::ProcessRecord(const UserRecord& user) {
|
||||
throw std::runtime_error("SQLite export service is not initialized");
|
||||
}
|
||||
|
||||
const sqlite3_int64 city_id = ResolveCityId(user.location);
|
||||
const sqlite3_int64 city_id = ResolveCityId(user.address.city);
|
||||
|
||||
sqlite_export_service_internal::Bind(
|
||||
insert_user_stmt_,
|
||||
sqlite_export_service_internal::BoundParam<sqlite3_int64>{
|
||||
.index = sqlite_export_service_internal::kUserCityIdBindIndex,
|
||||
.value = city_id,
|
||||
.action = "Failed to bind SQLite user city id"});
|
||||
sqlite_export_service_internal::Bind(
|
||||
insert_user_stmt_,
|
||||
sqlite_export_service_internal::BoundParam<std::string_view>{
|
||||
@@ -73,7 +67,28 @@ uint64_t SqliteExportService::ProcessRecord(const UserRecord& user) {
|
||||
sqlite_export_service_internal::StepStatement(
|
||||
db_handle_, insert_user_stmt_, "Failed to insert SQLite user row");
|
||||
|
||||
const sqlite3_int64 user_id =
|
||||
sqlite_export_service_internal::LastInsertRowId(db_handle_);
|
||||
sqlite_export_service_internal::ResetStatement(insert_user_stmt_);
|
||||
|
||||
return sqlite_export_service_internal::LastInsertRowId(db_handle_);
|
||||
sqlite_export_service_internal::Bind(
|
||||
insert_user_address_stmt_,
|
||||
sqlite_export_service_internal::BoundParam<sqlite3_int64>{
|
||||
.index = sqlite_export_service_internal::kUserAddressUserIdBindIndex,
|
||||
.value = user_id,
|
||||
.action = "Failed to bind SQLite user address user id"});
|
||||
sqlite_export_service_internal::Bind(
|
||||
insert_user_address_stmt_,
|
||||
sqlite_export_service_internal::BoundParam<sqlite3_int64>{
|
||||
.index = sqlite_export_service_internal::kUserAddressCityIdBindIndex,
|
||||
.value = city_id,
|
||||
.action = "Failed to bind SQLite user address city id"});
|
||||
|
||||
sqlite_export_service_internal::StepStatement(
|
||||
db_handle_, insert_user_address_stmt_,
|
||||
"Failed to insert SQLite user address row");
|
||||
|
||||
sqlite_export_service_internal::ResetStatement(insert_user_address_stmt_);
|
||||
|
||||
return user_id;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user