diff --git a/src/xvtdb/xvtdb.cpp b/src/xvtdb/xvtdb.cpp new file mode 100644 index 000000000..6486dfd78 --- /dev/null +++ b/src/xvtdb/xvtdb.cpp @@ -0,0 +1,554 @@ +#include "xvtdb.h" +#include + +#define _CON(a) ((SAConnection *)a) +#define _RCS(a) ((SACommand *)a) +#define _ERR(a) ((SAException *)a) + +// Funzione per la connessione, utilizzando un DBMS devo controllare sempre +int xvt_create_connection(P_CONN_VOID con, const char* db, const char* user, const char* pass, TT_driver tipoDb) +{ + 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)tipoDb; + if (dbAddress.IsEmpty() || usr.IsEmpty()) + { + return NOT_INITIALIZED; + } + 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); + } + catch (SAException &x) + { + return x.ErrNativeCode(); + } + return NOERR; + } + return NOT_INITIALIZED; +} + + +/****************************************************************************** + * TXvt_recordset * + * Classe per esecuzioni di query temporanee (wrapper semplice per SACommand) * + ******************************************************************************/ + +TXvt_recordset::TXvt_recordset() +{ + _con = new SAConnection; + _recset = new SACommand; + try + { + _RCS(_recset)->setConnection(_CON(_con)); + } + catch (SAException &x) + { + _codeError = x.ErrNativeCode(); + _stringError = x.ErrMessage(); + } +} + + +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) +{ + _con = new SAConnection; + if(xvt_create_connection(_con, 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) + { + _codeError = x.ErrNativeCode(); + _stringError = x.ErrMessage(); + } + } +} + +TXvt_recordset::TXvt_recordset(P_CONN_VOID c, const char * query, bool ex) +{ + _toDisconnect = true; + //_con = c; + memcpy(_con, c, sizeof(c)); + if (!_CON(_con)->isConnected()) + bool tolla = true; + _recno = -1; + try + { + _recset = new SACommand; + _RCS(_recset)->setConnection(_CON(_con)); + // if (query[0] != '\0') + if (query && *query) + { + set(query); + if (ex) + { + exec(); + } + } + } + catch (SAException &x) + { + _codeError = x.ErrNativeCode(); + _stringError = x.ErrMessage(); + } +} + +TXvt_recordset::~TXvt_recordset() +{ + try + { + if(_toDisconnect && _CON(_con)->isConnected()) + _CON(_con)->Disconnect(); + if(_con != NULL) + delete _con; + if(_recset != NULL) + delete _recset; + } + catch(...){} +} + +/* PRIVATE FUNCTIONS **************************************************************************************************/ + +const bool TXvt_recordset::checkPermission() +{ + bool err = isFreezed(); + if(err) + { + _codeError = ERROR_FREEZED; + _stringError = "Il recordset è bloccato"; + } + return !err; +} + +/* PUBLIC FUNCTIONS **************************************************************************************************/ + +/************************************************************************************************** + * Gestione Connection * + **************************************************************************************************/ + +int TXvt_recordset::connect(const char* db, const char* user, const char* pass, TT_driver tipoDb) +{ + return xvt_create_connection(_con, db, user, pass, tipoDb); +} + +void TXvt_recordset::disconnect() +{ + try + { + _CON(_con)->Disconnect(); + } + catch(SAException &x) + { + _codeError = x.ErrNativeCode(); + _stringError = x.ErrMessage(); + } +} + +bool TXvt_recordset::commit(bool autoRoll) +{ + try + { + _CON(_con)->Commit(); + } + catch (SAException &x) + { + if (autoRoll) + { + rollback(); + } + _codeError = x.ErrNativeCode(); + _stringError = x.ErrMessage(); + return false; + } + return true; +} + +bool TXvt_recordset::rollback() +{ + try + { + _CON(_con)->Rollback(); + } + catch (SAException &x) + { + _codeError = x.ErrNativeCode(); + _stringError = x.ErrMessage(); + return false; + } + return true; +} + +void TXvt_recordset::setClient(TT_driver client) +{ + _CON(_con)->setClient((SAClient_t) client); +} + +void TXvt_recordset::setConOption(const char* opt) +{ + _CON(_con)->setOption(opt); +} + +void TXvt_recordset::setAutocommit(bool ac) +{ + _CON(_con)->setAutoCommit(ac ? SA_AutoCommitOn : SA_AutoCommitOff); +} + +void TXvt_recordset::setVisibility(isoLvl 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); +} + +bool TXvt_recordset::isConnect() +{ + return _CON(_con)->isConnected(); +} +bool TXvt_recordset::isAlive() +{ + return _CON(_con)->isAlive(); +} +int TXvt_recordset::getVisibility() +{ + return (isoLvl)_CON(_con)->IsolationLevel(); +} +bool TXvt_recordset::getAutocommit() +{ + return _CON(_con)->AutoCommit() == SA_AutoCommitOn; +} +const char* TXvt_recordset::getOption(const char* opt) +{ + return _CON(_con)->Option(opt); +} +long TXvt_recordset::getClientV() +{ + return _CON(_con)->ClientVersion(); +} +const char* TXvt_recordset::getServerV() +{ + return _CON(_con)->ServerVersionString(); +} +long TXvt_recordset::getServerVN() +{ + return _CON(_con)->ServerVersion(); +} + + +/************************************************************************************************** + * Gestione Recordset * + **************************************************************************************************/ +bool TXvt_recordset::set(const char* query) +{ + try + { + _RCS(_recset)->setCommandText(query); + } + catch (SAException &x) + { + _codeError = x.ErrNativeCode(); + _stringError = x.ErrMessage(); + return false; + } + return true; +} + +bool TXvt_recordset::exec(bool autoF) +{ + try + { + _RCS(_recset)->Execute(); + _recno = -1; + if (autoF) + { + next(); + } + } + catch (SAException &x) + { + _codeError = x.ErrNativeCode(); + _stringError = x.ErrMessage(); + return false; + } + return true; +} + +bool TXvt_recordset::next() +{ + bool fetched = false; + try + { + if(_RCS(_recset)->FetchNext()) + { + fetched = true; + _recno++; + } + + } + catch (SAException &x) + { + _codeError = x.ErrNativeCode(); + _stringError = x.ErrMessage(); + } + return fetched; +} + +bool TXvt_recordset::prev() +{ + bool fetched = false; + try + { + if (_RCS(_recset)->FetchPrior()) + { + fetched = true; + _recno--; + } + } + catch (SAException &x) + { + _codeError = x.ErrNativeCode(); + _stringError = x.ErrMessage(); + } + return fetched; +} + +bool TXvt_recordset::first() +{ + bool fetched = false; + try + { + if (_RCS(_recset)->FetchFirst()) + { + fetched = true; + _recno = 0; + } + } + catch (SAException &x) + { + _codeError = x.ErrNativeCode(); + _stringError = x.ErrMessage(); + } + 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) + { + _codeError = x.ErrNativeCode(); + _stringError = x.ErrMessage(); + } + return fetched; +} + +bool TXvt_recordset::go(int newPos) +{ + // Controllo che la nuova posizione non sia fuori dal limite inferiore (Non so quanto è grande il recordset) + bool result = newPos >= 0 ? true : false; + + // Se la posizione è minore mi sposto indietro + while (newPos < _recno && result) + { + result = prev(); + } + // Se la posizione è maggiore mi sposto in avanti + while (newPos > _recno && result) + { + result = next(); + } + /* + // Controllo finale per prevenire errori + if (newPos == _recno) + return true; + else + return false; + */ + return result; +} + +int TXvt_recordset::get_int(const char* field) +{ + try + { + return static_cast(_RCS(_recset)->Field(field).asLong()); + } + catch (SAException &x) + { + _codeError = x.ErrNativeCode(); + _stringError = x.ErrMessage(); + + return -1; + } +} + +short TXvt_recordset::get_short(const char* field) +{ + try + { + return _RCS(_recset)->Field(field).asShort(); + } + catch (SAException &x) + { + _codeError = x.ErrNativeCode(); + _stringError = x.ErrMessage(); + + return -1; + } +} + +long TXvt_recordset::get_long(const char* field) +{ + try + { + return _RCS(_recset)->Field(field).asLong(); + } + catch (SAException &x) + { + _codeError = x.ErrNativeCode(); + _stringError = x.ErrMessage(); + + return -1; + } +} + +double TXvt_recordset::get_double(const char* field) +{ + try + { + return _RCS(_recset)->Field(field).asDouble(); + } + catch (SAException &x) + { + _codeError = x.ErrNativeCode(); + _stringError = x.ErrMessage(); + return -1; + } +} + +bool TXvt_recordset::get_bool(const char* field) +{ + try + { + return _RCS(_recset)->Field(field).asBool(); + } + catch (SAException &x) + { + _codeError = x.ErrNativeCode(); + _stringError = x.ErrMessage(); + return false; + } +} +/* +real TXvt_recordset::GetReal(const char * field) +{ + return Get(field); +} +*/ +const char* TXvt_recordset::getDate(const char * field) +{ + try + { + SADateTime app = _RCS(_recset)->Field(field).asDateTime(); + char date[10]; + sprintf_s(date, (size_t)10, "%d-%d-%d", app.GetDay(), app.GetMonth(), app.GetYear()); + return date; + } + catch (SAException &x) + { + _codeError = x.ErrNativeCode(); + _stringError = x.ErrMessage(); + return NULL; + } +} + +const char* TXvt_recordset::get(const char* field) +{ + try + { + return static_cast(_RCS(_recset)->Field(field).asString()); + } + catch (SAException &x) + { + _codeError = x.ErrNativeCode(); + _stringError = x.ErrMessage(); + return ""; + } +} + +char TXvt_recordset::get_char(const char* field) +{ + try + { + return _RCS(_recset)->Field(field).asString()[0]; + } + catch (SAException &x) + { + _codeError = x.ErrNativeCode(); + _stringError = x.ErrMessage(); + return '\0'; + } +} + +long TXvt_recordset::getCodeError(bool erase) +{ + long app = _codeError; + if (erase) + _codeError = NOERR; + return app; +} + +const char* TXvt_recordset::getStringError(bool erase) +{ + const char* app = _stringError.c_str(); + if (erase) + _stringError.erase(); + return app; +} \ No newline at end of file diff --git a/src/xvtdb/xvtdb.h b/src/xvtdb/xvtdb.h new file mode 100644 index 000000000..b881a7ab9 --- /dev/null +++ b/src/xvtdb/xvtdb.h @@ -0,0 +1,198 @@ +#pragma once +#include + +#ifndef NOERR +#define NOERR 0 +#endif + +#define NOT_INITIALIZED -86552 +#define ERROR_FREEZED -883 + +enum TT_driver +{ + //! DBMS client is not specified + TSDB_undefined, + //! ODBC + TSDB_ODBC, + //! Oracle + TSDB_Oracle, + //! Microsoft SQL Server + TSDB_MSSQL, + //! InterBase or Firebird + TSDB_InterBase, + //! SQLBase + TSDB_SQLBase, + //! IBM DB2 + TSDB_DB2, + //! Informix + TSDB_Informix, + //! Sybase ASE + TSDB_Sybase, + //! MySQL + TSDB_MySQL, + //! PostgreSQL + TSDB_PostgreSQL, + //! SQLite + TSDB_SQLite, + //! SQL Anywere + TSDB_SQLAnywhere +}; + +enum isoLvl { unknown = -1, uncommitted, committed, rr, serializable }; + +#define P_CONN_VOID void * +#define P_COMM_VOID void * + +/* Esempio parametri da passare con MSSQL: + * db = SERVER@DATABASE (NBKDELLI7TOLLAR\MSSQLSERVER14@project_vahlalla) + */ +int xvt_create_connection(P_CONN_VOID con, const char* db, const char* user, const char* pass, TT_driver tipoDb); + + +/****************************************************************************** + * TXvt_recordset : DataBase redcordset * + * Wrapper base di SACommand e SAConnection, classe per l'esecuzione di query * + * permette una connessione a vari DB tramite molteplici drivers * + ******************************************************************************/ +class TXvt_recordset +{ +protected: + /**< Recordset */ + P_COMM_VOID _recset; + /**< Oggetto della connessione */ + P_COMM_VOID _con; + /**< Ultima stringa con codice di errore ricevuto */ + std::string _stringError; + /**< Ultimo codice di errore ricevuto */ + long _codeError; + /**< Numero record corrente */ + long _recno; + /**< Indica se va disconnessa la classe durante la chiusura */ + bool _toDisconnect; + /**< Indica se l'oggetto è bloccato, in tal caso non si possono fare set/exec */ + bool _freezed; + /**< Indica se il cursore è caricato */ + bool _loaded; + + const bool checkPermission(); + +public: + /**< Costruttore, crea un oggetto vuoto, PERICOLOSO!! Ricordarsi di inizializzarlo opportunamente con il metodo connect() e set() */ + TXvt_recordset(); + /**< Costruttore, crea un oggetto dai parametri di connessione passati */ + TXvt_recordset(const char* db, const char* user, const char* pass, TT_driver tipoDb, const char * query = "", const bool ex = false, const bool freezed = false); + /**< Costruttore, accetta in ingresso una connessione, volendo è anche possibile impostare una query ed eseguirla. Attenzione! Non risponde se la query ha avuto un esito positivo o negativo! */ + TXvt_recordset(P_CONN_VOID c, const char * query = "", bool ex = false); + /**< Distruttore */ + virtual ~TXvt_recordset(); + + /************************************************************************************************** + * Gestione Connection * + **************************************************************************************************/ + + // Funzioni fondamentali + + /**< Eseguo la connessione */ + int connect(const char* db, const char* user, const char* pass, TT_driver tipoDb); + /* Mi scollego */ + void disconnect(); + /**< Esegue la commit, di default in caso di errore viene chiamato il metodo rollback() */ + bool commit(bool autoRoll = true); + /**< Esegue il rollback all'ultimo commit */ + bool rollback(); + + // Setters + + /**< Imposto il tipo di client che utilizzo */ + void setClient(TT_driver client); + // Imposto una opzione generica dellla connessione + void setConOption(const char* opt); + /**< Abilito/Disabilito l'autocommit, (disabilitato di default) */ + void setAutocommit(bool ac); + /** Imposta la visibilità delle transazioni (vedi Funzione) */ + void setVisibility(isoLvl vis = committed); + + + // Getters + /** Ritorna se la conessione è connessa */ + bool isConnect(); + /**< Ritorna se la connessione è attiva */ + bool isAlive(); + /** Ritorna la visibilità impostata */ + int getVisibility(); + /**< Ritorna se è attivo l'autocommit, true -> attivo, false -> disattivo o sconosciuto */ + bool getAutocommit(); + /**< Ritorna il valore dell'opzione specificata */ + const char* getOption(const char* opt); + /**< Ritorno la versione del Client che sto utilizzando */ + long getClientV(); + /**< Ritorno la versione del Server che sto utilizzando */ + const char* getServerV(); + /**< Ritorno la versione del Server che sto utilizzando in formato numerico */ + long getServerVN(); + + + /************************************************************************************************** + * Gestione Recordset * + **************************************************************************************************/ + + // Conguration + /**< Imposta la query ricevuta come (const char *) nel recordset */ + bool set(const char* query); + /**< Imposta la query ricevuta come (string) nel recordset */ + bool set(std::string query) { return set(query.c_str()); } + /**< Esegue la query impostata nel recordset, se viene passato autoF == true esegue anche un comando Next() */ + bool exec(bool autoF = true); + /**< Unisce le funzioni Set e Exec, riceve la query come (const char *) */ + bool setExec(const char* query, bool autoF = true) { set(query); return exec(autoF); } + /**< Unisce le funzioni Set e Exec, riceve la query come (string) */ + bool setExec(std::string query, bool autoF = true) { set(query); return exec(autoF); } + /**< Si sposta avanti di un record, in caso di esito negativo valorizza _stringError e _codeError */ + bool next(); + /**< Si sposta indietro di un record, in caso di esito negativo valorizza _stringError e _codeError */ + bool prev(); + /**< Si sposta avanti di un record, in caso di esito negativo valorizza _stringError e _codeError */ + bool first(); + /**< Si sposta al primo record, in caso di esito negativo valorizza _stringError e _codeError */ + bool last(); + /**< Si sposta alla posizione n, in caso di esito negativo valorizza _stringError e _codeError */ + bool go(int newPos); + + // Getters + /**< Ritorna il valore nel campo (field) in formato (int) */ + int get_int(const char* field); + /**< Ritorna il valore nel campo (field) in formato (short) */ + short get_short(const char* field); + /**< Ritorna il valore nel campo (field) in formato (long) */ + long get_long(const char* field); + /**< Ritorna il valore nel campo (field) in formato (double) */ + double get_double(const char* field); + /**< Ritorna il valore nel campo (field) in formato (bool) */ + bool get_bool(const char* field); + /**< Ritorna il valore nel campo (field) in formato (const char *)->(DD-MM-YYYY) */ + const char* getDate(const char* field); + /**< Ritorna il valore nel campo (field) passato come (const char *) in formato (const char *) */ + const char* get(const char* field); + /**< Ritorna il valore nel campo (field) passato come (string) in formato (const char *) */ + const char* get(std::string field) { return get(field.c_str()); } + /**< Ritorna il valore nel campo (field) in formato (char) */ + char get_char(const char* field); + /**< Ritorna la posizione attuale */ + long pos() const { return _recno; } + + // Error Handling + /**< Ritorno l'ultimo codice errore segnalato in formato /int) */ + long getCodeError(bool erase = true); + /**< Ritorno l'ultima stringa di errore segnalato in formato (const char *) */ + const char* getStringError(bool erase = true); + //char * getCharPointer(const char * field) { return const_cast(static_cast(recset.Field(field).asString())); } + + // Utilities + /**< Controlla se il cursore è bloccato */ + const bool isFreezed() const { return _freezed; } + /**< Congela il cursore */ + void freeze() { _freezed = true; } + /**< Scongela il cursore */ + void defrost() { _freezed = false; } + +}; \ No newline at end of file