#include "xvtdb.h" #include #include // sprintf_s #define _CON(a) ((SAConnection *)a) #define _RCS(a) ((SACommand *)a) #define _ERR(a) ((SAException *)a) #define _CPY_STR(from,to) (to = strdup(from)); #define _GET_ERROR(from,to) _CPY_STR(from,to) #define CHECK_FREEZED if (is_freezed()) { return set_error_freezed(); } /****************************************************************************** * TXvt_recordset * * Classe per esecuzioni di query temporanee (wrapper semplice per SACommand) * ******************************************************************************/ TXvt_recordset::TXvt_recordset() : _freezed(false) { _con = new SAConnection; _recset = new SACommand; try { _RCS(_recset)->setConnection(_CON(_con)); } catch (SAException &x) { _code_error = x.ErrNativeCode(); _GET_ERROR(x.ErrMessage(), _string_error); _GET_ERROR(x.ErrText(), _string_error_full_text); } } TXvt_recordset::TXvt_recordset(const char* db, const char* user, const char* pass, TT_driver tipoDb, const char * query, const bool ex, const bool freezed) : _db(db), _usr(user), _psw(pass), _drv(tipoDb), _freezed(false) { _con = new SAConnection; if(connect(db, user, pass, tipoDb) == NOERR) { try { _recset = new SACommand; _RCS(_recset)->setConnection(_CON(_con)); // if (query[0] != '\0') if (query && *query) { set(query); if (ex) { exec(); // Terribile da vedere, ma ho fatto una funzione apposta e la voglio usare per Diana! if(freezed) freeze(); } } } catch (SAException &x) { _code_error = x.ErrNativeCode(); _GET_ERROR(x.ErrMessage(), _string_error); _GET_ERROR(x.ErrText(), _string_error_full_text); } } } TXvt_recordset::~TXvt_recordset() { try { // Se non è connesso viene lanciata l'eccezione if(_CON(_con)->isConnected()) _CON(_con)->Disconnect(); } catch (...) {} // Prima cancellare il recordset POI la connessione delete _recset; delete _con; } /* PRIVATE FUNCTIONS **************************************************************************************************/ const bool TXvt_recordset::checkPermission() { bool err = is_freezed(); if (err) set_error_freezed(); return !err; } /* PUBLIC FUNCTIONS **************************************************************************************************/ /************************************************************************************************** * Gestione Connection * **************************************************************************************************/ int TXvt_recordset::connect(const char* db, const char* user, const char* pass, TT_driver tipo_db) { if (_con != NULL) { // Se è già connesso lo scollego if (_CON(_con)->isConnected()) _CON(_con)->Disconnect(); SAString dbAddress = db; SAString usr = user; SAString psw = pass; SAClient_t dbDriver = (SAClient_t)tipo_db; if (dbAddress.IsEmpty() || usr.IsEmpty()) { _code_error = NOT_INITIALIZED; _string_error = NOT_INITIALIZEDS; } else { try { // Mi collego _CON(_con)->Connect(dbAddress, usr, psw, dbDriver); // Imposto che non si possono vedere i record non committati _CON(_con)->setIsolationLevel(SA_ReadCommitted); return NOERR; } catch (SAException &x) { _code_error = x.ErrNativeCode(); _GET_ERROR(x.ErrMessage(), _string_error); _GET_ERROR(x.ErrText(), _string_error_full_text); } } } else { _code_error = NOT_INITIALIZED; _string_error = NOT_INITIALIZEDS; } return _code_error; } void TXvt_recordset::disconnect() { try { _CON(_con)->Disconnect(); } catch(SAException &x) { _code_error = x.ErrNativeCode(); _GET_ERROR(x.ErrMessage(), _string_error); _GET_ERROR(x.ErrText(), _string_error_full_text); } } bool TXvt_recordset::commit(bool autoRoll) { CHECK_FREEZED try { _CON(_con)->Commit(); } catch (SAException &x) { if (autoRoll) { rollback(); } _code_error = x.ErrNativeCode(); _GET_ERROR(x.ErrMessage(), _string_error); _GET_ERROR(x.ErrText(), _string_error_full_text); return false; } return true; } bool TXvt_recordset::rollback() { CHECK_FREEZED try { _CON(_con)->Rollback(); } catch (SAException &x) { _code_error = x.ErrNativeCode(); _GET_ERROR(x.ErrMessage(), _string_error); _GET_ERROR(x.ErrText(), _string_error_full_text); return false; } return true; } void TXvt_recordset::set_client(TT_driver client) { _CON(_con)->setClient((SAClient_t) client); } void TXvt_recordset::set_con_option(const char* opt) { _CON(_con)->setOption(opt); } void TXvt_recordset::set_autocommit(bool ac) { _CON(_con)->setAutoCommit(ac ? SA_AutoCommitOn : SA_AutoCommitOff); } void TXvt_recordset::set_visibility(iso_lvl vis) { /* La libreria attuale supporta diversi tipi di visibilità, per mantenere una compatibilità massima * (evitando inutili seghe mentali) consiglio di usare i primi due, di default la classe imposta "Read committed" * Tipi di isolazione: * 0 -> Read uncommitted. * 1 -> Read committed. * 2 -> Repeatable read. * 3 -> Serializable. */ _CON(_con)->setIsolationLevel((SAIsolationLevel_t)vis); } const bool TXvt_recordset::is_connect() const { return _CON(_con)->isConnected(); } const bool TXvt_recordset::is_alive() const { return _CON(_con)->isAlive(); } int TXvt_recordset::get_visibility() { return (iso_lvl)_CON(_con)->IsolationLevel(); } bool TXvt_recordset::get_autocommit() { return _CON(_con)->AutoCommit() == SA_AutoCommitOn; } const char* TXvt_recordset::get_option(const char* opt) { return _CON(_con)->Option(opt); } long TXvt_recordset::get_client_v() { return _CON(_con)->ClientVersion(); } const char* TXvt_recordset::getServerV() { return _CON(_con)->ServerVersionString(); } long TXvt_recordset::get_server_vn() { return _CON(_con)->ServerVersion(); } bool TXvt_recordset::is_loaded() const { return _loaded; } long TXvt_recordset::items() { // Per non creare casini utilizzo una funzione apposita che mi ritorna il numero P_CONN_VOID conn = new SAConnection; char q[500]; long items; // Connetto la nuova istanza _CON(conn)->Connect(_db, _usr, _psw, (SAClient_t)_drv); // Controllo sia tutto a posto // Creo la query strcat_s(q, sizeof(q), "SELECT COUNT(*) "); strcat_s(q, sizeof(q), (strstr(_query, "FROM") != NULL ? strstr(_query, "FROM") : strstr(_query, "from"))); // Serve? items = xvt_rcs_get_items(conn, q); if(conn != _con) delete conn; return items; } long xvt_rcs_get_items(P_CONN_VOID con, const char* query) { P_COMM_VOID rcs = new SACommand(_CON(con)); _RCS(rcs)->setCommandText(query); long r = 0; for(bool ok = _RCS(rcs)->FetchFirst(); ok; ok = _RCS(rcs)->FetchNext()) r++; delete rcs; return r; } /************************************************************************************************** * Gestione Recordset * **************************************************************************************************/ bool TXvt_recordset::set(const char* query) { CHECK_FREEZED try { _query = query; _RCS(_recset)->setCommandText(query); } catch (SAException &x) { _code_error = x.ErrNativeCode(); _GET_ERROR(x.ErrMessage(), _string_error); _GET_ERROR(x.ErrText(), _string_error_full_text); return false; } return true; } bool TXvt_recordset::exec(bool autoF) { CHECK_FREEZED bool ok = false; try { _RCS(_recset)->Execute(); _recno = -1; // Se trovo almeno un "select" faccio l'autofetch SAString s = _RCS(_recset)->CommandText(); s.MakeUpper(); ok = s.Find("SELECT") != SIZE_MAX && autoF ? next() : true; } catch (SAException &x) { _code_error = x.ErrNativeCode(); _GET_ERROR(x.ErrMessage(), _string_error); _GET_ERROR(x.ErrText(), _string_error_full_text); ok = false; } return ok; } bool TXvt_recordset::set_exec(const char* query, bool auto_f) { set(query); return exec(auto_f); } bool TXvt_recordset::next() { bool fetched = false; try { if(_RCS(_recset)->FetchNext()) { fetched = true; _recno++; } } catch (SAException &x) { _code_error = x.ErrNativeCode(); _GET_ERROR(x.ErrMessage(), _string_error); _GET_ERROR(x.ErrText(), _string_error_full_text); } return fetched; } bool TXvt_recordset::prev() { bool fetched = false; try { if (_RCS(_recset)->FetchPrior()) { fetched = true; _recno--; } } catch (SAException &x) { _code_error = x.ErrNativeCode(); _GET_ERROR(x.ErrMessage(), _string_error); _GET_ERROR(x.ErrText(), _string_error_full_text); } return fetched; } bool TXvt_recordset::first() { bool fetched = false; try { if (_RCS(_recset)->FetchFirst()) { fetched = true; _recno = 0; } } catch (SAException &x) { _code_error = x.ErrNativeCode(); _GET_ERROR(x.ErrMessage(), _string_error); _GET_ERROR(x.ErrText(), _string_error_full_text); } return fetched; } bool TXvt_recordset::last() { /* La vita sarebbe molto bella se potessi chiamare la funzione FetchLast(), // siccome non so la posizione del record eseguo ciclicamente Next // _RCS(_recset)->FetchLast(); while (Next()) { _recno++; } */ bool fetched = false; try { if (_RCS(_recset)->FetchLast()) { fetched = true; _recno = 0; } } catch (SAException &x) { _code_error = x.ErrNativeCode(); _GET_ERROR(x.ErrMessage(), _string_error); _GET_ERROR(x.ErrText(), _string_error_full_text); } return fetched; } bool TXvt_recordset::go(int new_pos) { // Controllo che la nuova posizione non sia fuori dal limite inferiore (Non so quanto è grande il recordset) bool result = new_pos >= 0 ? true : false; // Se la posizione è minore mi sposto indietro while (new_pos < _recno && result) { result = prev(); } // Se la posizione è maggiore mi sposto in avanti while (new_pos > _recno && result) { result = next(); } /* // Controllo finale per prevenire errori if (newPos == _recno) return true; else return false; */ return result; } const int TXvt_recordset::rows_affected() const { return _RCS(_recset)->RowsAffected(); } int TXvt_recordset::get_int(const char* field) { try { return static_cast(_RCS(_recset)->Field(field).asLong()); } catch (SAException &x) { _code_error = x.ErrNativeCode(); _GET_ERROR(x.ErrMessage(), _string_error); _GET_ERROR(x.ErrText(), _string_error_full_text); return -1; } } short TXvt_recordset::get_short(const char* field) { try { return _RCS(_recset)->Field(field).asShort(); } catch (SAException &x) { _code_error = x.ErrNativeCode(); _GET_ERROR(x.ErrMessage(), _string_error); _GET_ERROR(x.ErrText(), _string_error_full_text); return -1; } } long TXvt_recordset::get_long(const char* field) { try { return _RCS(_recset)->Field(field).asLong(); } catch (SAException &x) { _code_error = x.ErrNativeCode(); _GET_ERROR(x.ErrMessage(), _string_error); _GET_ERROR(x.ErrText(), _string_error_full_text); return -1; } } double TXvt_recordset::get_double(const char* field) { try { return _RCS(_recset)->Field(field).asDouble(); } catch (SAException &x) { _code_error = x.ErrNativeCode(); _GET_ERROR(x.ErrMessage(), _string_error); _GET_ERROR(x.ErrText(), _string_error_full_text); return -1; } } bool TXvt_recordset::get_bool(const char* field) { try { return _RCS(_recset)->Field(field).asBool(); } catch (SAException &x) { _code_error = x.ErrNativeCode(); _GET_ERROR(x.ErrMessage(), _string_error); _GET_ERROR(x.ErrText(), _string_error_full_text); return false; } } /* real TXvt_recordset::GetReal(const char * field) { return Get(field); } */ const char* TXvt_recordset::get_date(const char * field) { try { SADateTime app = _RCS(_recset)->Field(field).asDateTime(); static char date[9]; sprintf_s(date, sizeof(date), "%04d%02d%02d", app.GetYear(), app.GetMonth(), app.GetDay()); return date; } catch (SAException &x) { _code_error = x.ErrNativeCode(); _GET_ERROR(x.ErrMessage(), _string_error); _GET_ERROR(x.ErrText(), _string_error_full_text); return NULL; } } const char* TXvt_recordset::get(const char* field) { SACommand& pop = *static_cast(_recset); try { static SAString fld; fld = _RCS(_recset)->Field(field).asString(); return static_cast(fld); } catch (SAException &x) { _code_error = x.ErrNativeCode(); _GET_ERROR(x.ErrMessage(), _string_error); _GET_ERROR(x.ErrText(), _string_error_full_text); return ""; } } char TXvt_recordset::get_char(const char* field) { try { return _RCS(_recset)->Field(field).asString()[0]; } catch (SAException &x) { _code_error = x.ErrNativeCode(); _GET_ERROR(x.ErrMessage(), _string_error); _GET_ERROR(x.ErrText(), _string_error_full_text); return '\0'; } } long TXvt_recordset::get_code_error(bool erase) { long app = _code_error; if (erase) _code_error = NOERR; return app; } const char* TXvt_recordset::get_string_error(bool erase) { static char* app; if (app != nullptr) delete app; _CPY_STR(_string_error, app); if (erase) _string_error = ""; return app; } const char* TXvt_recordset::get_text_error(bool erase) { static char* app; if (app != nullptr) delete app; _CPY_STR(_string_error_full_text, app); if (erase) _string_error_full_text = ""; return app; } const bool TXvt_recordset::set_error_freezed() { _code_error = ERROR_FREEZED; _string_error = _string_error_full_text = ERROR_FREEZEDS; return false; }