#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, const TT_driver tipo_db, const char * query, const bool ex, const bool freezed) : _db(db), _usr(user), _psw(pass), _drv(tipo_db), _query(""), _recno(0), _freezed(false), _loaded(false) { _con = new SAConnection; if(connect(db, user, pass, tipo_db) == NOERR) { try { _recset = new SACommand; _RCS(_recset)->setConnection(_CON(_con)); 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); } } } TT_driver TXvt_recordset::str_to_driver(const char* tipo_db) { TT_driver tipoDb_driver = TSDB_undefined; //! DBMS client is not specified if (_stricmp(tipo_db, "") != 0) tipoDb_driver = TSDB_undefined; //! ODBC else if (_stricmp(tipo_db, "ODBC") == 0) tipoDb_driver = TSDB_ODBC; //! Oracle else if (_stricmp(tipo_db, "Oracle") == 0) tipoDb_driver = TSDB_Oracle; //! Microsoft SQL Server else if (_stricmp(tipo_db, "MSSQL") == 0) tipoDb_driver = TSDB_MSSQL; //! InterBase or Firebird else if (_stricmp(tipo_db, "InterBase") == 0) tipoDb_driver = TSDB_InterBase; //! SQLBase else if (_stricmp(tipo_db, "SQLBase") == 0) tipoDb_driver = TSDB_SQLBase; //! IBM DB2 else if (_stricmp(tipo_db, "DB2") == 0) tipoDb_driver = TSDB_DB2; //! Informix else if (_stricmp(tipo_db, "Informix") == 0) tipoDb_driver = TSDB_Informix; //! Sybase ASE else if (_stricmp(tipo_db, "Sybase") == 0) tipoDb_driver = TSDB_Sybase; //! MySQL else if (_stricmp(tipo_db, "MySQL") == 0) tipoDb_driver = TSDB_MySQL; //! PostgreSQL else if (_stricmp(tipo_db, "PostgreSQL") == 0) tipoDb_driver = TSDB_PostgreSQL; //! SQLite else if (_stricmp(tipo_db, "SQLite") == 0) tipoDb_driver = TSDB_SQLite; //! SQL Anywere else if (_stricmp(tipo_db, "SQLAnywhere") == 0) tipoDb_driver = TSDB_SQLAnywhere; return tipoDb_driver; } TXvt_recordset::TXvt_recordset(const char* db, const char* user, const char* pass, const char* tipo_db, const char* query, const bool ex, const bool freezed) : TXvt_recordset(db, user, pass, str_to_driver(tipo_db), query, ex, freezed) { } 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::check_permission() { const 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 != nullptr) { // Se è già connesso lo scollego if (_CON(_con)->isConnected()) _CON(_con)->Disconnect(); SAString db_address = db; SAString usr = user; const SAString psw = pass; const SAClient_t db_driver = static_cast(tipo_db); if (db_address.IsEmpty() || usr.IsEmpty()) { _code_error = NOT_INITIALIZED; _string_error = NOT_INITIALIZEDS; } else { try { // Mi collego _CON(_con)->Connect(db_address, usr, psw, db_driver); // Imposto che non si possono vedere i record non committati _CON(_con)->setIsolationLevel(SA_ReadCommitted); _CPY_STR(db, _db); _CPY_STR(user, _usr); _CPY_STR(pass, _psw); _drv = tipo_db; 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(const bool auto_roll) { CHECK_FREEZED try { _CON(_con)->Commit(); } catch (SAException &x) { if (auto_roll) { 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) const { _CON(_con)->setClient(static_cast(client)); } void TXvt_recordset::set_con_option(const char* opt, const char* val) const { _CON(_con)->setOption(opt) = val; } void TXvt_recordset::set_autocommit(const bool ac) const { _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(static_cast(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() const { return static_cast(_CON(_con)->IsolationLevel()); } bool TXvt_recordset::get_autocommit() const { return _CON(_con)->AutoCommit() == SA_AutoCommitOn; } const char* TXvt_recordset::get_option(const char* opt) const { return _CON(_con)->Option(opt); } long TXvt_recordset::get_client_v() const { return _CON(_con)->ClientVersion(); } const char* TXvt_recordset::get_server_v() const { return _CON(_con)->ServerVersionString(); } long TXvt_recordset::get_server_vn() const { 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; SAString q; long items = DEFAULT_ERR_NUMBER; // Connetto la nuova istanza try { _CON(conn)->Connect(_db, _usr, _psw, static_cast(_drv)); items = xvt_rcs_get_items(conn, _query); if (conn != _con) delete conn; } catch(SAException &x) { _code_error = x.ErrNativeCode(); _GET_ERROR(x.ErrMessage(), _string_error); _GET_ERROR(x.ErrText(), _string_error_full_text); return false; } 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); _RCS(rcs)->Execute(); long r = 0; while(_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); _loaded = false; } 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(const bool auto_f) { CHECK_FREEZED bool ok = false; try { _RCS(_recset)->Execute(); _loaded = true; _recno = -1; // Se trovo almeno un "select" faccio l'autofetch, ovviamente non mi sbatto se auto_f è disattivato if (auto_f) { SAString s = _RCS(_recset)->CommandText(); s.MakeUpper(); ok = s.Find("SELECT") != SIZE_MAX ? next() : true; } else ok = 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, const 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() { 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(const 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; // 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(); } 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 DEFAULT_ERR_NUMBER; } } 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 DEFAULT_ERR_NUMBER; } } 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 DEFAULT_ERR_NUMBER; } } 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 DEFAULT_ERR_NUMBER; } } 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; } } 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 DEFAULT_ERR_STRING; } } 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 DEFAULT_ERR_STRING; } } const char* TXvt_recordset::get(unsigned int field) { try { static SAString str; str = _RCS(_recset)->Field(field + 1).asString(); return str; } catch(SAException &x) { _code_error = x.ErrNativeCode(); _GET_ERROR(x.ErrMessage(), _string_error); _GET_ERROR(x.ErrText(), _string_error_full_text); return DEFAULT_ERR_STRING; } } 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 DEFAULT_ERR_CHAR; } } int TXvt_recordset::get_num_fields() const { return _RCS(_recset)->FieldCount(); } const char* TXvt_recordset::get_name_field(const unsigned column) const { SAField& f_name = _RCS(_recset)->Field(column + 1); return static_cast(f_name.Name()); } int TXvt_recordset::get_width_field(const unsigned column) const { SAField& f_name = _RCS(_recset)->Field(column + 1); return static_cast(f_name.FieldSize()); } const char* TXvt_recordset::get_type_field(const unsigned column) const { SAField& f_name = _RCS(_recset)->Field(column + 1); const auto type = f_name.FieldType(); switch(type) { case SA_dtUnknown: return "dtUnknown"; case SA_dtBool: return "dtBool"; case SA_dtShort: return "dtShort"; case SA_dtUShort: return "dtLong"; case SA_dtULong: return "dtDouble"; case SA_dtNumeric: return "dtNumeric"; case SA_dtDateTime: return "dtDateTime"; case SA_dtString: return "dtString"; case SA_dtInterval: return "dtString"; case SA_dtBytes: return "dtBytes"; case SA_dtLongBinary: return "dtLongBinary"; case SA_dtLongChar: return "dtLongChar"; case SA_dtBLob: return "dtBLob"; case SA_dtCLob: return "dtCLob"; case SA_dtCursor: return "dtCursor"; case SA_dtSpecificToDBMS: return "dtSpecificToDBMS"; default: return "dtUnknown"; } } int TXvt_recordset::get_precision_field(const unsigned column) const { SAField& f_name = _RCS(_recset)->Field(column + 1); return f_name.FieldPrecision(); } int TXvt_recordset::get_scale_field(const unsigned column) const { SAField& f_name = _RCS(_recset)->Field(column + 1); return f_name.FieldScale(); } long TXvt_recordset::get_code_error(const bool erase) { const 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; }