#pragma once #include "util.hpp" #include #include #include #include #include #include namespace ikarus::util { struct EmptyNameError {}; COMPOUND_ERROR(InsertObjectError, EmptyNameError, sqlitecpp::TransactionError); template [[nodiscard]] cppbase::Result, InsertObjectError> insert_object( IkarusProject * project, IkarusObjectType type, std::string_view name, InsertFunction insert_function, ObjectFactory object_factory ) { auto const * object_type_str = ikarus_object_type_to_string(type); LOG_INFO("creating new {}", object_type_str); LOG_DEBUG("project={}; name={}", project->get_path().c_str(), name); auto * ctx = project->get_function_context(); if (cppbase::is_empty_or_blank(name)) { ctx->set_error("name is empty or blank", true, IkarusErrorInfo_Source_Client, IkarusErrorInfo_Type_Client_Input); return cppbase::err(EmptyNameError{}); } VTRY( auto const id, project->get_db() ->transact([&](auto * db) -> cppbase::Result { LOG_VERBOSE("creating {} in objects table", object_type_str); TRY(db->execute("INSERT INTO `objects` (`object_type`, `name`) VALUES(?, ?);", static_cast(type), name)); auto id = ikarus_id_from_data_and_type(db->last_insert_rowid(), IkarusObjectType_Blueprint); LOG_DEBUG("{} is {}", object_type_str, id); TRY(insert_function(db, id)); return cppbase::ok(id); }) .on_error([&](auto const & err) { ctx->set_error( fmt::format("unable to insert {} into database: {}", object_type_str, err), true, IkarusErrorInfo_Source_SubSystem, IkarusErrorInfo_Type_SubSystem_Database ); }) ); LOG_VERBOSE("successfully created {}", object_type_str); return cppbase::ok(object_factory(id)); } template requires std::derived_from void delete_object(IkarusProject * project, Object * object) { auto object_type_str = ikarus_object_type_to_string(ikarus_id_get_object_type(object->id)); LOG_INFO("deleting {}", object_type_str); LOG_DEBUG("project={}; {}={}", object_type_str, object->project->get_path().c_str(), object->id); auto * ctx = object->project->get_function_context(); TRYRV(, project->get_db()->execute("DELETE FROM `objects` WHERE `id` = ?", object->id).on_error([&](auto const & err) { ctx->set_error( fmt::format("failed to delete {} from objects table: {}", object_type_str, err), true, IkarusErrorInfo_Source_SubSystem, IkarusErrorInfo_Type_SubSystem_Database ); })); LOG_VERBOSE("{} was successfully deleted from database, freeing", object_type_str); project->uncache(object); LOG_VERBOSE("successfully deleted {}", object_type_str); } struct SingleQueryData { std::string_view table_name; std::string_view select_field_name; }; template requires std::derived_from cppbase::Result fetch_single_field(Object const * object, SingleQueryData const & query_data) { auto object_type_str = ikarus_object_type_to_string(ikarus_id_get_object_type(object->id)); LOG_VERBOSE("fetching property default value"); LOG_VERBOSE("project={};property={}", object->project->get_path().c_str(), object->id); auto * ctx = object->project->get_function_context(); VTRY( T value, object->project->get_db() ->template query_one( fmt::format("SELECT `{}` FROM `{}` WHERE `id` = ?", query_data.select_field_name, query_data.table_name), object->id ) .on_error([&](auto const & err) { ctx->set_error( fmt::format("failed to fetch {} {} from database: {}", object_type_str, query_data.select_field_name, err), true, IkarusErrorInfo_Source_SubSystem, IkarusErrorInfo_Type_SubSystem_Database ); }) ); return value; } struct MultipleBufferQueryData { std::string_view table_name; std::string_view select_field_name; std::string_view where_field_name; std::string_view relation_desc; }; template requires std::derived_from void fetch_multiple_buffered( Object const * object, MultipleBufferQueryData const & query_data, Mapped * mapped_buffer, size_t buffer_size, F transformer ) requires cppbase::is_result_with_value_type_v> { auto * ctx = object->project->get_function_context(); auto object_type_str = ikarus_object_type_to_string(ikarus_id_get_object_type(object->id)); LOG_VERBOSE("fetching {} {}", object_type_str, query_data.relation_desc); LOG_VERBOSE("project={};{}={}", object->project->get_path().c_str(), object_type_str, object->id); Selected select_buffer[buffer_size]; TRYRV( , object->project->get_db() ->template query_many_buffered( fmt::format( "SELECT `{}` FROM `{}` WHERE `{}` = ?", query_data.select_field_name, query_data.table_name, query_data.where_field_name ), select_buffer, buffer_size, object->id ) .on_error([&](auto const & err) { ctx->set_error( fmt::format("failed to fetch {} {} from database: {}", object_type_str, query_data.relation_desc, err), true, IkarusErrorInfo_Source_SubSystem, IkarusErrorInfo_Type_SubSystem_Database ); }) ); LOG_DEBUG("{} {}: [{}]", object_type_str, query_data.relation_desc, fmt::join(select_buffer, select_buffer + buffer_size, ", ")); for (size_t i = 0; i < buffer_size; ++i) { VTRYRV(mapped_buffer[i], , transformer(object->project, ctx, select_buffer[i])); } } struct CountQueryData { std::string_view table_name; std::string_view select_field_name; std::string_view where_field_name; std::string_view relation_desc; }; template requires std::derived_from cppbase::Result fetch_count(Object const * object, CountQueryData const & query_data) { auto * object_type_str = ikarus_object_type_to_string(ikarus_id_get_object_type(object->id)); LOG_VERBOSE("fetching {} {} count", object_type_str, query_data.relation_desc); auto * ctx = object->project->get_function_context(); LOG_DEBUG("{}={}", object_type_str, object->id); VTRY( auto count, object->project->get_db() ->template query_one( fmt::format( "SELECT COUNT(`{}`) FROM `{}` WHERE `{}` = ?;", query_data.select_field_name, query_data.table_name, query_data.where_field_name ), object->id ) .on_error([&](auto const & err) { ctx->set_error( fmt::format("failed to fetch {} {} count: {}", object_type_str, query_data.relation_desc, err), true, IkarusErrorInfo_Source_SubSystem, IkarusErrorInfo_Type_SubSystem_Database ); }) ); LOG_DEBUG("{} {} count: {}", object_type_str, query_data.relation_desc, count); LOG_VERBOSE("successfully fetched {} {} count", object_type_str, query_data.relation_desc); return cppbase::ok(static_cast(count)); } } // namespace ikarus::util