Making progress with shop snapshots

This commit is contained in:
King_DuckZ 2020-08-15 01:55:46 +01:00
parent afb4fd2d4d
commit 928cabfe34
2 changed files with 123 additions and 31 deletions

View file

@ -26,16 +26,20 @@
#include "oro/creators.hpp"
#include "oro/private/dateconv.hpp"
#include "oro/private/tiger.hpp"
#include "oro/private/sqlite_helpers.hpp"
#include "duckhandy/int_conv.hpp"
#include <mutex>
#include <optional>
#include <type_traits>
#include <unordered_map>
#include <cstdint>
#include <utility>
namespace oro {
namespace {
typedef decltype(std::declval<SQLite::Column>().getInt64()) SQLiteID;
constexpr const char g_create_fame_list[] =
"CREATE TABLE IF NOT EXISTS fame_list("
"id INTEGER PRIMARY KEY NOT NULL"
@ -86,7 +90,6 @@ namespace {
", loc_x INTEGER"
", loc_y INTEGER"
", type TINYINT"
", seen_date TEXT DEFAULT CURRENT_TIMESTAMP"
")";
constexpr const char g_create_items_a[] = "CREATE TABLE IF NOT EXISTS ";
constexpr const char g_create_items_b[] =
@ -157,20 +160,42 @@ namespace {
bool m_use_cache;
};
template <typename CastT=void, typename T=void>
void bind (SQLite::Statement& st, int idx, const std::optional<T>& val) {
if (val) {
if constexpr (std::is_same_v<T, CastT> or std::is_same_v<void, CastT>) {
st.bind(idx, *val);
class InsertShopIfn {
public:
explicit InsertShopIfn (SQLite::Database& db) :
m_sel(db, "SELECT id FROM shops WHERE creation_date = ? AND owner = ?"),
m_ins(db, "INSERT INTO shops(title, owner, creation_date, loc_map, loc_x, loc_y, type) VALUES(?, ?, ?, ?, ?, ?, ?)"),
m_db(db)
{
}
std::pair<SQLiteID, bool> operator() (const oro::Shop& shop) {
std::string shop_creation_date = to_sqlite_string(shop.creation_date);
auto row_id_opt = exec_first_res<SQLiteID>(m_sel, shop_creation_date, shop.owner);
if (row_id_opt) {
return {*row_id_opt, false};
}
else {
st.bind(idx, static_cast<CastT>(*val));
m_ins.bind(1, shop.title);
m_ins.bind(2, shop.owner);
m_ins.bindNoCopy(3, shop_creation_date);
m_ins.bind(4, shop.location.map);
m_ins.bind(5, shop.location.x);
m_ins.bind(6, shop.location.y);
m_ins.bind(7, static_cast<int>(shop.type.value));
m_ins.exec();
m_ins.reset();
return {m_db.getLastInsertRowid(), true};
}
}
else {
st.bind(idx, nullptr);
}
}
private:
SQLite::Statement m_sel;
SQLite::Statement m_ins;
const SQLite::Database& m_db;
};
std::optional<oro::Shop> fetch_shop (
SQLite::Database& db,
@ -364,37 +389,28 @@ void OriginsDB::update (const Shops& shops) {
Database db(m_db_mutex, m_path);
db.exec(g_create_shops);
db.exec(g_create_shop_snapshots);
db.exec("CREATE UNIQUE INDEX IF NOT EXISTS shops_owner_idx ON shops(owner, creation_date)");
db.exec(g_create_shop_snapshots);
db.exec("CREATE UNIQUE INDEX IF NOT EXISTS shop_snapshots_shop_hash_idx ON shop_snapshots(shop_id, hash)");
db.exec(g_create_shop_items);
db.exec(g_create_slotted_cards);
SQLite::Statement ins_shop(db, "INSERT INTO shops(title, owner, creation_date, loc_map, loc_x, loc_y, type) VALUES(?, ?, ?, ?, ?, ?, ?)");
SQLite::Statement ins_sshot(db, "INSERT INTO shop_snapshots(shop_id, hash) VALUES(?, ?)");
SQLite::Statement ins_sshot(db, "INSERT OR IGNORE INTO shop_snapshots(shop_id, hash) VALUES(?, ?)");
SQLite::Statement sel_sshot(db, "SELECT id FROM shop_snapshots WHERE shop_id = ? AND hash = ?");
SQLite::Statement ins_item(db, "INSERT INTO shop_items(snapshot_id, item_id, amount, price, refine, star_crumbs, element, creator, beloved) VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?)");
SQLite::Statement ins_card(db, "INSERT INTO slotted_cards(shop_item_id, card_id) VALUES(?, ?)");
InsertShopIfn insert_shop_ifn(db);
ItemIdToTableId item_id_to_table_id(db);
SQLite::Transaction transaction(db);
for (const auto& shop : shops.shops) {
std::optional<oro::Shop> old_shop = fetch_shop(db, shop.owner, shop.creation_date);
if (old_shop)
continue;
ins_shop.bind(1, shop.title);
ins_shop.bind(2, shop.owner);
ins_shop.bind(3, to_sqlite_string(shop.creation_date));
ins_shop.bind(4, shop.location.map);
ins_shop.bind(5, shop.location.x);
ins_shop.bind(6, shop.location.y);
ins_shop.bind(7, static_cast<int>(shop.type.value));
ins_shop.exec();
ins_shop.reset();
{
const auto shop_id = db.getLastInsertRowid();
ins_sshot.bind(1, shop_id);
const auto shop_id_ins = insert_shop_ifn(shop);
auto sshot_id_ins = exec_first_res<SQLiteID>(sel_sshot, shop_id_ins.first, shop_hash(shop));
if (sshot_id_ins)
continue;
ins_sshot.bind(1, shop_id_ins.first);
ins_sshot.bind(2, shop_hash(shop));
ins_sshot.exec();
ins_sshot.reset();

View file

@ -0,0 +1,76 @@
/* Copyright 2020, Michele Santullo
* This file is part of orotool.
*
* Orotool is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Orotool is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Orotool. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "SQLiteCpp/Statement.h"
#include <optional>
#include <utility>
#include <type_traits>
namespace oro {
namespace detail {
template <typename CastT=void, typename T=void>
void bind (SQLite::Statement& st, int idx, T&& val) {
if constexpr (std::is_same_v<std::decay_t<T>, std::decay_t<CastT>> or std::is_same_v<void, CastT>) {
st.bind(idx, std::forward<T>(val));
}
else {
st.bind(idx, static_cast<CastT>(val));
}
}
template <typename CastT=void, typename T=void>
void bind (SQLite::Statement& st, int idx, const std::optional<T>& val) {
if (val)
detail::bind (st, idx, *val);
else
st.bind(idx, nullptr);
}
template <int... Indices, typename... Args>
void bind_many (SQLite::Statement& st, std::integer_sequence<int, Indices...>, Args&&... args) {
const int dummy = (
bind(st, Indices + 1, std::forward<Args>(args)), ..., 0
);
static_cast<void>(dummy);
}
} //namespace detail
template <typename... Args>
inline void bind (SQLite::Statement& st, Args&&... args) {
detail::bind_many(st, std::make_integer_sequence<int, sizeof...(Args)>{}, std::forward<Args>(args)...);
}
template <typename CastT=void, typename T=void>
inline void bind (SQLite::Statement& st, int index, T&& val) {
detail::bind<CastT>(st, index, std::forward<T>(val));
}
template <typename T, typename... Args>
inline std::optional<T> exec_first_res (SQLite::Statement& st, Args&&... args) {
detail::bind_many(st, std::make_integer_sequence<int, sizeof...(Args)>{}, std::forward<Args>(args)...);
std::optional<T> retval;
if (st.executeStep()) {
retval = st.getColumn(0);
}
st.reset();
return retval;
}
} //namespace oro