/* * This file is a part of morm * and is distributed under the 2-Clause BSD licence. * Author: Tomasz Sowa */ /* * Copyright (c) 2018-2021, Tomasz Sowa * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * */ #ifndef headerfile_morm_cursor #define headerfile_morm_cursor #include "queryresult.h" #include "modelconnector.h" namespace morm { template class Cursor { public: Cursor() { query_result = nullptr; clear(); } Cursor(const Cursor & c) { model_connector = c.model_connector; model_data = c.model_data; has_autogenerated_select = c.has_autogenerated_select; use_table_prefix_for_fetching = c.use_table_prefix_for_fetching; query_result = c.query_result; select_status = c.select_status; if( query_result ) { query_result->references_count += 1; } // helper doesn't have to be copied cursor_helper.clear(); finder_helper.clear(); } Cursor & operator=(const Cursor & c) = delete; virtual ~Cursor() { if( query_result && query_result->references_count > 0 ) { query_result->references_count -= 1; } clear(); } virtual void clear() { if( query_result && query_result->references_count == 0 ) { query_result->clear(); delete query_result; } model_connector = nullptr; model_data = nullptr; has_autogenerated_select = false; use_table_prefix_for_fetching = false; cursor_helper.clear(); finder_helper.clear(); query_result = nullptr; select_status = false; } virtual void set_model_connector(ModelConnector * model_connector) { this->model_connector = model_connector; } virtual void set_model_data(ModelData * model_data) { this->model_data = model_data; } virtual void set_query_result(QueryResult * query_result) { this->query_result = query_result; } virtual void set_select_status(bool select_status) { this->select_status = select_status; } virtual void set_has_autogenerated_select(bool has_autogenerated_select) { this->has_autogenerated_select = has_autogenerated_select; } virtual void use_table_prefix(bool use_table_prefix_for_fetching) { this->use_table_prefix_for_fetching = use_table_prefix_for_fetching; } virtual QueryResult * get_query_result() { return query_result; } virtual bool has_next() { bool has = false; if( model_connector && query_result && query_result->has_db_result() && select_status ) { has = query_result->cur_row < query_result->result_rows; } return has; } virtual bool get(ModelClass & result) { bool res = false; result.set_connector(model_connector); result.clear(); result.save_mode = Model::DO_NOTHING_ON_SAVE; if( model_connector && query_result && query_result->has_db_result() ) { DbConnector * db_connector = model_connector->get_db_connector(); if( db_connector ) { ModelEnv model_env_local; result.model_env = &model_env_local; result.model_env->cursor_helper = &cursor_helper; result.model_env->finder_helper = &finder_helper; try { finder_helper.clear(); // at the moment used only for calculating table prefixes (indices) cursor_helper.clear(); cursor_helper.query_result = query_result; cursor_helper.has_autogenerated_select = has_autogenerated_select; cursor_helper.use_table_prefix_for_fetching_values = use_table_prefix_for_fetching; result.model_env->model_data = model_data; if( !cursor_helper.has_autogenerated_select && cursor_helper.use_table_prefix_for_fetching_values ) { result.model_env->add_table_name_to_finder_helper(); } result.before_select(); res = select_status; if( res ) { if( query_result->cur_row < query_result->result_rows ) { result.map_values_from_query(); if( result.found() ) { result.after_select(); } query_result->cur_row += 1; } else { res = false; // log some error or throw an exception? } } else { result.after_select_failure(); } } catch(...) { res = false; // throw something? } result.model_env = nullptr; } } return res; } virtual ModelClass get() { ModelClass model; get(model); return model; } virtual bool get_list(std::list & result, bool clear_list = true) { return get_list_generic(result, clear_list); } virtual std::list get_list() { std::list result; get_list(result, false); return result; } virtual bool get_vector(std::vector & result, bool clear_list = true) { if( query_result && query_result->has_db_result() && result.capacity() < query_result->result_rows ) { result.reserve(query_result->result_rows); } return get_list_generic(result, clear_list); } virtual std::vector get_vector() { std::vector result; get_vector(result, false); return result; } protected: ModelConnector * model_connector; ModelData * model_data; bool has_autogenerated_select; bool use_table_prefix_for_fetching; CursorHelper cursor_helper; FinderHelper finder_helper; // may CursorHelper and FinderHelper should be one class? QueryResult * query_result; bool select_status; template bool get_list_generic(ContainerType & result, bool clear_list = true) { bool res = false; if( clear_list ) { result.clear(); } if( model_connector && query_result && query_result->has_db_result() ) { DbConnector * db_connector = model_connector->get_db_connector(); if( db_connector ) { res = select_status; try { if( res ) { res = add_models_to_list(result); } else { // add log // if there was a failure we do not call after_select_failure() // because there are no any objects in the list } } catch(...) { res = false; // throw or something? } } } return res; } template bool add_models_to_list(ContainerType & result) { bool res = true; for(query_result->cur_row = 0 ; query_result->cur_row < query_result->result_rows ; query_result->cur_row += 1) { result.emplace_back(); // it returns a reference from c++17 ModelClass & added_model = result.back(); ModelEnv model_env_local; try { finder_helper.clear(); cursor_helper.clear(); cursor_helper.query_result = query_result; cursor_helper.has_autogenerated_select = has_autogenerated_select; cursor_helper.use_table_prefix_for_fetching_values = use_table_prefix_for_fetching; added_model.set_connector(model_connector); added_model.clear(); added_model.model_env = &model_env_local; added_model.model_env->cursor_helper = &cursor_helper; added_model.model_env->finder_helper = &finder_helper; added_model.model_env->model_data = model_data; if( !cursor_helper.has_autogenerated_select && cursor_helper.use_table_prefix_for_fetching_values ) { added_model.model_env->add_table_name_to_finder_helper(); } added_model.before_select(); added_model.map_values_from_query(); if( added_model.found() ) { added_model.after_select(); } } catch(...) { res = false; // throw or something? } added_model.model_env = nullptr; } return res; } }; } // namespace #endif