/* * This file is a part of morm * and is distributed under the 2-Clause BSD licence. * Author: Tomasz Sowa */ /* * Copyright (c) 2018-2023, 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_src_cursor #define headerfile_morm_src_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; last_query_status = c.last_query_status; select_flags = c.select_flags; rows_counter = c.rows_counter; cursor_name = c.cursor_name; scroll_cursor = c.scroll_cursor; 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; last_query_status = false; select_flags = Select::default_type; rows_counter = 0; cursor_name.clear(); scroll_cursor = 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_last_query_status(bool status) { this->last_query_status = 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 void set_select_flags(const Select & select_flags) { this->select_flags = select_flags; } virtual void set_rows_counter_column_name(const std::wstring & column_name) { this->rows_counter_column_name = column_name; } 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() && last_query_status ) { has = query_result->cur_row < query_result->result_rows; } return has; } virtual void prepare_model_env_for_new_object(ModelEnv & model_env) { model_env.cursor_helper = &cursor_helper; model_env.finder_helper = &finder_helper; model_env.select_flags = select_flags; model_env.rows_counter = 0; model_env.model_data = model_data; if( model_env.select_flags.is_with_rows_counter() ) { if( cursor_helper.current_row == 0 ) { model_env.rows_counter_column_name = rows_counter_column_name; } } } 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 ) { try { ModelEnv model_env_local; result.model_env = &model_env_local; result.model_env->model = &result; result.model_env->model_data = model_data; 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; cursor_helper.current_row = 0; prepare_model_env_for_new_object(model_env_local); if( !cursor_helper.has_autogenerated_select && cursor_helper.use_table_prefix_for_fetching_values ) { result.table(); result.model_env->add_table_name_to_finder_helper(); } result.before_select(); res = last_query_status; if( res ) { if( query_result->cur_row < query_result->result_rows ) { result.map_values_from_query(); rows_counter = model_env_local.rows_counter; 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; } virtual size_t get_rows_counter() { return rows_counter; } virtual void set_cursor_name(pt::TextStream & cursor_name) { this->cursor_name = cursor_name; } virtual void set_scroll_cursor(bool scroll_cursor) { this->scroll_cursor = scroll_cursor; } virtual Cursor & fetch(const char * fetch_str) { last_query_status = false; rows_counter = 0; if( query_result ) { query_result->clear(); if( model_connector ) { DbConnector * db_connector = model_connector->get_db_connector(); if( db_connector ) { bool status = db_connector->query_select(fetch_str, *query_result); last_query_status = status; } } } return *this; } virtual Cursor & fetch(pt::TextStream & str) { if( str.size() < 256 ) { char str_buf[256]; if( str.to_str(str_buf, sizeof(str_buf) / sizeof(char)) ) { fetch(str_buf); } } else { put_cursor_fetch_query_too_long(); } return *this; } virtual Cursor & fetch(pt::WTextStream & str) { if( str.size() < 256 ) { char str_buf[256]; if( str.to_str(str_buf, sizeof(str_buf) / sizeof(char)) ) { fetch(str_buf); } } else { put_cursor_fetch_query_too_long(); } return *this; } virtual Cursor & fetch_next() { return fetch(&DbExpression::prepare_fetch_next_query); } virtual Cursor & fetch_prior() { return fetch(&DbExpression::prepare_fetch_prior_query); } virtual Cursor & fetch_first() { return fetch(&DbExpression::prepare_fetch_first_query); } virtual Cursor & fetch_last() { return fetch(&DbExpression::prepare_fetch_last_query); } virtual Cursor & fetch_absolute(long position) { return fetch(&DbExpression::prepare_fetch_absotule_query, position); } virtual Cursor & fetch_relative(long position) { return fetch(&DbExpression::prepare_fetch_relative_query, position); } virtual Cursor & fetch_forward_count(size_t len) { return fetch(&DbExpression::prepare_fetch_forward_count_query, len); } virtual Cursor & fetch_backward_count(size_t len) { return fetch(&DbExpression::prepare_fetch_backward_count_query, len); } virtual Cursor & fetch_all() { return fetch(&DbExpression::prepare_fetch_all_query); } 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 last_query_status; Select select_flags; size_t rows_counter; std::wstring rows_counter_column_name; pt::TextStream cursor_name; bool scroll_cursor; typedef void (DbExpression::*prepare_fetch_method_type)(const pt::TextStream & cursor_name, pt::TextStream & out_stream); typedef void (DbExpression::*prepare_fetch_long_count_method_type)(const pt::TextStream & cursor_name, long count, pt::TextStream & out_stream); typedef void (DbExpression::*prepare_fetch_size_count_method_type)(const pt::TextStream & cursor_name, size_t count, pt::TextStream & out_stream); DbExpression * get_db_expression() { if( model_connector ) { DbConnector * db_connector = model_connector->get_db_connector(); if( db_connector ) { return db_connector->get_expression(); } } return nullptr; } virtual Cursor & fetch(prepare_fetch_method_type prepare_fetch_method) { if( !cursor_name.empty() ) { DbExpression * db_expression = get_db_expression(); if( db_expression ) { pt::TextStream str; (db_expression->*prepare_fetch_method)(cursor_name, str); fetch(str); } } else { put_cursor_not_declared_log(); } return *this; } virtual Cursor & fetch(prepare_fetch_long_count_method_type prepare_fetch_method, long count) { if( !cursor_name.empty() ) { DbExpression * db_expression = get_db_expression(); if( db_expression ) { pt::TextStream str; (db_expression->*prepare_fetch_method)(cursor_name, count, str); fetch(str); } } else { put_cursor_not_declared_log(); } return *this; } virtual Cursor & fetch(prepare_fetch_size_count_method_type prepare_fetch_method, size_t count) { if( !cursor_name.empty() ) { DbExpression * db_expression = get_db_expression(); if( db_expression ) { pt::TextStream str; (db_expression->*prepare_fetch_method)(cursor_name, count, str); fetch(str); } } else { put_cursor_not_declared_log(); } return *this; } 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 = last_query_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; cursor_helper.current_row = query_result->cur_row; added_model.set_connector(model_connector); added_model.clear(); prepare_model_env_for_new_object(model_env_local); added_model.model_env = &model_env_local; added_model.model_env->model = &added_model; added_model.model_env->model_data = model_data; if( !cursor_helper.has_autogenerated_select && cursor_helper.use_table_prefix_for_fetching_values ) { added_model.table(); added_model.model_env->add_table_name_to_finder_helper(); } added_model.before_select(); added_model.map_values_from_query(); if( model_env_local.select_flags.is_with_rows_counter() && query_result->cur_row == 0 ) { rows_counter = model_env_local.rows_counter; } if( added_model.found() ) { added_model.after_select(); } } catch(...) { res = false; // throw or something? } added_model.model_env = nullptr; } return res; } pt::Log * get_logger() { if( model_connector ) { return model_connector->get_logger(); } return nullptr; } void put_log(pt::Log::Manipulators level, const char * msg) { pt::Log * plog = get_logger(); if( plog ) { (*plog) << level << msg << pt::Log::logend; } } void put_cursor_fetch_query_too_long() { put_log(pt::Log::log2, "Morm: error: a fetch query should be less than 256 characters long"); } void put_cursor_not_declared_log() { put_log(pt::Log::log2, "Morm: you cannot use fetch* methods, a cursor has not been declared, use Finder::declare_cursor(...) to declare a cursor"); } }; } // namespace #endif