From 928cabfe341e4464cd353a4ab5cb3fba0259a455 Mon Sep 17 00:00:00 2001 From: King_DuckZ Date: Sat, 15 Aug 2020 01:55:46 +0100 Subject: [PATCH] Making progress with shop snapshots --- src/oro/originsdb.cpp | 78 ++++++++++++++++++------------ src/oro/private/sqlite_helpers.hpp | 76 +++++++++++++++++++++++++++++ 2 files changed, 123 insertions(+), 31 deletions(-) create mode 100644 src/oro/private/sqlite_helpers.hpp diff --git a/src/oro/originsdb.cpp b/src/oro/originsdb.cpp index c60a510..28dea2e 100644 --- a/src/oro/originsdb.cpp +++ b/src/oro/originsdb.cpp @@ -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 #include #include #include #include +#include namespace oro { namespace { + typedef decltype(std::declval().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 - void bind (SQLite::Statement& st, int idx, const std::optional& val) { - if (val) { - if constexpr (std::is_same_v or std::is_same_v) { - 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 operator() (const oro::Shop& shop) { + std::string shop_creation_date = to_sqlite_string(shop.creation_date); + + auto row_id_opt = exec_first_res(m_sel, shop_creation_date, shop.owner); + if (row_id_opt) { + return {*row_id_opt, false}; } else { - st.bind(idx, static_cast(*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(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 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 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(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(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(); diff --git a/src/oro/private/sqlite_helpers.hpp b/src/oro/private/sqlite_helpers.hpp new file mode 100644 index 0000000..8566a2f --- /dev/null +++ b/src/oro/private/sqlite_helpers.hpp @@ -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 . + */ + +#pragma once + +#include "SQLiteCpp/Statement.h" +#include +#include +#include + +namespace oro { +namespace detail { + template + void bind (SQLite::Statement& st, int idx, T&& val) { + if constexpr (std::is_same_v, std::decay_t> or std::is_same_v) { + st.bind(idx, std::forward(val)); + } + else { + st.bind(idx, static_cast(val)); + } + } + + template + void bind (SQLite::Statement& st, int idx, const std::optional& val) { + if (val) + detail::bind (st, idx, *val); + else + st.bind(idx, nullptr); + } + + template + void bind_many (SQLite::Statement& st, std::integer_sequence, Args&&... args) { + const int dummy = ( + bind(st, Indices + 1, std::forward(args)), ..., 0 + ); + static_cast(dummy); + } +} //namespace detail + +template +inline void bind (SQLite::Statement& st, Args&&... args) { + detail::bind_many(st, std::make_integer_sequence{}, std::forward(args)...); +} + +template +inline void bind (SQLite::Statement& st, int index, T&& val) { + detail::bind(st, index, std::forward(val)); +} + +template +inline std::optional exec_first_res (SQLite::Statement& st, Args&&... args) { + detail::bind_many(st, std::make_integer_sequence{}, std::forward(args)...); + + std::optional retval; + if (st.executeStep()) { + retval = st.getColumn(0); + } + + st.reset(); + return retval; +} +} //namespace oro