#include #include #include #include #include /////////////////////////////////////////////////////////// // TODBC_connections /////////////////////////////////////////////////////////// class TODBC_connections { TString _dsn, _usr; XVT_ODBC _odbc; protected: void close(); public: XVT_ODBC get(const char* dsn, const char* usr, const char* pwd, const char* dir); TODBC_connections(); ~TODBC_connections(); }; TODBC_connections _connections; void TODBC_connections::close() { if (_odbc != NULL) { xvt_odbc_free_connection(_odbc); _odbc = NULL; } } XVT_ODBC TODBC_connections::get(const char* dsn, const char* usr, const char* pwd, const char* dir) { if (_odbc == NULL || _dsn != dsn || _usr != usr) { close(); if (dsn && *dsn) _odbc = xvt_odbc_get_connection(_dsn = dsn, _usr = usr, pwd, dir); } return _odbc; } TODBC_connections::TODBC_connections() : _odbc(NULL) {} TODBC_connections::~TODBC_connections() { close(); } /////////////////////////////////////////////////////////// // TODBC_recordset /////////////////////////////////////////////////////////// XVT_ODBC TODBC_recordset::connection() const { return _connections.get(_dsn, _usr, _pwd, _dir); } bool TODBC_recordset::connect(const char* dsn, const char* usr, const char* pwd, const char* dir) { _dsn = dsn; _usr = usr; _pwd = pwd; _dir = dir; return connection() != NULL; } const TString& TODBC_recordset::query_text() const { return _sql; } const TString& TODBC_recordset::driver_version() const { TString& tmp = get_tmp_string(50); xvt_odbc_driver(connection(), tmp.get_buffer(), tmp.size()); return tmp; } void TODBC_recordset::reset() { _first_row = 0; _items = 0; _current_row = -1; _pagesize = 512; _page.destroy(); _column.destroy(); _columns_loaded = false; } int TODBC_recordset::on_get_columns(int argc, char** values, char** columns) { static unsigned long _counter = 0; if (_column.empty()) { _counter = 0; for (int i = 0; i < argc; i++) { const char* fldtype = NULL; TRecordset_column_info* info = new TRecordset_column_info; info->_width = 1; info->_type = _alfafld; if (columns != NULL) { info->_name = columns[i]; fldtype = columns[argc+i]; } else info->_name.format("FIELD%d", i+1); if (fldtype != NULL) { if (xvt_str_compare_ignoring_case(fldtype, "DATE") == 0) { info->_type = _datefld; info->_width = 10; } else if (xvt_str_compare_ignoring_case(fldtype, "NUMERIC") == 0) { info->_type = _realfld; } else if (xvt_str_compare_ignoring_case(fldtype, "BLOB") == 0) { info->_type = _memofld; info->_width = 50; } } _column.add(info); } } const bool processed = _counter++ < 128; if (processed) { for (int i = 0; i < argc; i++) if (values[i] && *values[i]) { TRecordset_column_info& info = (TRecordset_column_info&)_column[i]; if (info._type == _alfafld || info._type == _realfld) { const int len = strlen(values[i]); if (len > info._width) info._width = len; } } } return processed ? 0 : -1; } static int query_get_columns(void* jolly, int argc, char** values, char** columns) { TODBC_recordset* q = (TODBC_recordset*)jolly; return q->on_get_columns(argc, values, columns); } void TODBC_recordset::requery() { _items = 0; _current_row = -1; _page.destroy(); } int TODBC_recordset::on_get_items(int argc, char** values, char** columns) { if (!_columns_loaded) on_get_rows(argc, values, columns); return 0; } static int query_get_items(void* jolly, int argc, char** values, char** columns) { TODBC_recordset* q = (TODBC_recordset*)jolly; return q->on_get_items(argc, values, columns); } TRecnotype TODBC_recordset::items() const { if (_items == 0) { TString sql; parsed_text(sql); XVT_ODBC oc = connection(); if (oc != NULL) { TPerformance_profiler prof("ODBC count"); TRecnotype& i = (TRecnotype&)_items; if (!_columns_loaded) { TODBC_recordset* myself = (TODBC_recordset*)this; myself->_page.destroy(); myself->_cursor_pos = 0; i = xvt_odbc_execute(oc, sql, query_get_items, (void*)this); myself->_columns_loaded = true; } else i = xvt_odbc_execute(oc, sql, NULL, NULL); } } return _items; } unsigned int TODBC_recordset::columns() const { if (!_columns_loaded && _column.items() == 0) { XVT_ODBC oc = connection(); if (oc != NULL) { TODBC_recordset* myself = (TODBC_recordset*)this; TString sql; parsed_text(sql); TPerformance_profiler prof("ODBC info"); myself->_cursor_pos = 0; xvt_odbc_execute(oc, sql, query_get_columns, (void*)this); myself->_columns_loaded = true; } } return _column.items(); } const TRecordset_column_info& TODBC_recordset::column_info(unsigned int c) const { if (c >= columns()) // Generare column infos if needed c = 0; return (const TRecordset_column_info&)_column[c]; } // Funzione chiamata per riempire la pagina corrente delle righe della query int TODBC_recordset::on_get_rows(int argc, char** values, char** columns) { if (!_columns_loaded) on_get_columns(argc, values, columns); if (_page.items() >= _pagesize) return -1; if (_cursor_pos++ < _first_row) return 0; // Ignora le righe prima del LIMIT TArray* a = new TArray; for (int c = 0; c < argc; c++) { TVariant* var = new TVariant; switch (column_info(c)._type) { case _alfafld: var->set(values[c]); break; case _memofld: if (values[c]) { TFixed_string memo(values[c]); memo.replace(char(0xB6), '\n'); var->set(memo); } break; case _datefld: var->set(TDate(values[c])); break; default: var->set(real(values[c])); break; } a->add(var, c); } _page.add(a); return 0; } static int query_get_rows(void* jolly, int argc, char** values, char** columns) { TODBC_recordset* rs = (TODBC_recordset*)jolly; return rs->on_get_rows(argc, values, columns); } bool TODBC_recordset::move_to(TRecnotype n) { const TRecnotype tot = items(); _current_row = n; if (n < 0 || n >= tot) { _page.destroy(); // Forza rilettura la prossima volta _first_row = 0; return false; } if (n < _first_row || n >= _first_row+_page.items()) { TString sql; parsed_text(sql); XVT_ODBC oc = connection(); if (oc == NULL) return false; if (tot > _pagesize && sql.find("LIMIT ") < 0) { const int semicolon = sql.rfind(';'); if (semicolon >= 0) sql.cut(semicolon); sql.trim(); _page.destroy(); if (n >= _pagesize) _first_row = n-32; // Prendo qualche riga dalla pagina precedente, per velocizzare il pagina su else _first_row = n; } TPerformance_profiler prof("ODBC query"); _cursor_pos = 0; xvt_odbc_execute(oc, sql, query_get_rows, this); if (!_columns_loaded) _columns_loaded = true; // Brutto posto ma necessario } return true; } long TODBC_recordset::exec(const char* sql) { long err = -1; TString cmd; // Se la stringa "sql" non contiene parametri di connessione ma essi sono in "_sql" // allora cerco di effetture la connessione in base a quella if (_dsn.empty() && strncmp(sql, "ODBC(", 5) != 0 && _sql.starts_with("ODBC(")) parsed_text(cmd); XVT_ODBC oc = connection(); if (oc != NULL) { set(sql); parsed_text(cmd); TPerformance_profiler prof("ODBC command"); err = xvt_odbc_execute(oc, cmd, NULL, NULL); } return err; } TRecnotype TODBC_recordset::current_row() const { return _current_row; } const TArray* TODBC_recordset::row(TRecnotype n) { const TArray* a = NULL; if (move_to(n)) a = (const TArray*)_page.objptr(n-_first_row); return a; } const TVariant& TODBC_recordset::get(unsigned int c) const { const TArray* a = (const TArray*)_page.objptr(_current_row-_first_row); if (a != NULL) { const TVariant* s = (const TVariant*)a->objptr(c); if (s != NULL) return *s; } return NULL_VARIANT; } const TVariant& TODBC_recordset::get(const char* name) const { return TRecordset::get(name); } void TODBC_recordset::parsed_text(TString& sql) const { TRecordset::parsed_text(sql); if (sql.starts_with("ODBC(", true)) { const int par = sql.find(')'); if (par > 0) { TToken_string conn(sql.sub(5, par), ','); sql.ltrim(par+1); sql.trim(); TString dsn = conn.get(); dsn.strip("\""); TString usr = conn.get(); usr.strip("\""); TString pwd = conn.get(); pwd.strip("\""); TString dir = conn.get(); dir.strip("\""); if (!((TODBC_recordset*)this)->connect(dsn, usr, pwd, dir)) error_box(FR("Impossibile connettersi al DSN %s"), (const char*)dsn); } } } void TODBC_recordset::set(const char* sql) { reset(); _sql = sql; if (_sql.find("SELECT") >= 0 || _sql.find("select") >= 0) find_and_reset_vars(); } TODBC_recordset::TODBC_recordset(const char* sql) { set(sql); } TODBC_recordset::~TODBC_recordset() { } /////////////////////////////////////////////////////////// // Creazione "intelligente" del recordset appropriato in base alla query /////////////////////////////////////////////////////////// TRecordset* create_recordset(const TString& sql) { TRecordset* rex = NULL; if (sql.full()) { if (sql.starts_with("US", true)) rex = new TISAM_recordset(sql); else if (sql.starts_with("ODBC", true)) rex = new TODBC_recordset(sql); else if (sql.starts_with("CSV", true)) rex = new TCSV_recordset(sql); else if (sql.starts_with("AS400", true)) rex = new TAS400_recordset(sql); else rex = new TSQL_recordset(sql); } return rex; }