campo-sirio/include/odbcrset.cpp
bonazzi 6d56901048 Patch level : 10.0 270
Files correlati     :  ca2.exe ca2500a.msk
Ricompilazione Demo : [ ]
Commento            :

Aggiunto programma di invio a Board.
Ripristinate funzioni di visualizzazione e salvataggio ripartizione temporale delle righe nella modifica movimenti analitici

git-svn-id: svn://10.65.10.50/branches/R_10_00@23206 c028cbd2-c16b-5b4b-a496-9718f37d4682
2016-06-29 12:00:44 +00:00

746 lines
17 KiB
C++
Executable File

#include <diction.h>
#include <odbcrset.h>
#include <sqlset.h>
#include <textset.h>
#include <utility.h>
///////////////////////////////////////////////////////////
// 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;
bool ok = connection() != NULL;
if (ok)
{
const TString& name = driver_version();
if (name.starts_with("ACEODB"))
_driver = ODBC_access;
else
if (name.starts_with("SQLSRV"))
_driver = ODBC_mssql;
else
if (name.starts_with("MYSQL??"))
_driver = ODBC_mysql;
else
_driver = ODBC_generic;
}
return ok;
}
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_same(fldtype, "DATE"))
{
info->_type = _datefld;
info->_width = 10;
} else
if (xvt_str_same(fldtype, "NUMERIC"))
{
info->_type = _realfld;
} else
if (xvt_str_same(fldtype, "BLOB"))
{
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 (!_loaded && _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 (!_freezed && _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 (_freezed && _loaded)
{
if (n < 0)
_current_row = 0L;
if (n > tot)
_current_row = tot;
return true;
}
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()) || _freezed && !_loaded)
{
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 (_freezed)
_first_row = 0;
else
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);
_loaded = _freezed;
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;
}
long TODBC_recordset::begin()
{
if (driver() == ODBC_mssql)
return exec("BEGIN;");
return 0L;
}
long TODBC_recordset::commit()
{
if (driver() == ODBC_mssql)
return exec("COMMIT;");
return 0L;
}
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);
}
}
}
bool TODBC_recordset::set_log_file(const char* fn)
{
XVT_ODBC h = connection();
return xvt_odbc_log_file(h, fn) != 0;
}
int TODBC_recordset::compare_key(const TISAM_recordset& dbfset)
{
int cmp = 0;
const RecDes& recdes = dbfset.cursor()->curr().rec_des();
const int keyno = dbfset.cursor()->key();
const KeyDes& keydes = recdes.Ky[keyno-1];
const int col = columns();
for (int f = 0; f < keydes.NkFields && cmp == 0; f++)
{
const int nfield = keydes.FieldSeq[f] % MaxFields;
const char* fname = recdes.Fd[nfield].Name;
const bool upper = keydes.FieldSeq[f] > MaxFields;
switch (recdes.Fd[f].TypeF)
{
case _alfafld:
case _memofld:
if (upper)
cmp = xvt_str_compare_ignoring_case(dbfset.get(fname).as_string(), get(fname).as_string());
else
cmp = dbfset.get(fname).compare(get(fname));
break;
case _realfld:
{
const real dbfnum = dbfset.get(fname).as_real();
const real sqlnum = get(fname).as_real();
if (dbfnum != sqlnum)
cmp = dbfnum < sqlnum ? -1 : +1;
}
break;
case _datefld:
{
const TDate dbfdate = dbfset.get(fname).as_date();
const TDate sqldate = get(fname).as_date();
if (dbfdate != sqldate)
cmp = dbfdate < sqldate ? -1 : +1;
}
break;
default:
cmp = dbfset.get(fname).as_int() - get(fname).as_int();
break;
}
}
return cmp;
}
int TODBC_recordset::compare_rec(const TISAM_recordset& dbfset)
{
int cmp = 0;
const RecDes& recdes = dbfset.cursor()->curr().rec_des();
const unsigned int nfields = recdes.NFields;
const int col = columns();
for (unsigned int f = 0; f < nfields && cmp == 0; f++)
{
const TVariant& dbffld = dbfset.get(f);
const TVariant& sqlfld = get(f);
switch (recdes.Fd[f].TypeF)
{
case _alfafld:
case _memofld:
cmp = dbffld.compare(sqlfld);
break;
case _realfld:
{
const real dbfnum = dbffld.as_real();
const real sqlnum = sqlfld.as_real();
if (dbfnum != sqlnum)
cmp = dbfnum < sqlnum ? -1 : +1;
}
break;
case _datefld:
{
const TDate dbfdate = dbffld.as_date();
const TDate sqldate = sqlfld.as_date();
if (dbfdate != sqldate)
cmp = dbfdate < sqldate ? -1 : +1;
}
break;
default:
cmp = dbffld.as_int() - sqlfld.as_int();
break;
}
}
return cmp;
}
int TODBC_recordset::create_rec(const TISAM_recordset& dbfset)
{
const TRectype& curr = dbfset.cursor()->curr();
const int lf = curr.num();
TString table = prefix().get_filename(lf).name_only(); table.upper();
TString query(255); query << "INSERT INTO " << table << " VALUES(";
TODBC_driver drv = driver();
const RecDes& recdes = curr.rec_des();
const unsigned int nfields = recdes.NFields;
for (unsigned int f = 0; f < nfields; f++)
{
const char* fld = recdes.Fd[f].Name;
if (f) query << ',';
switch (recdes.Fd[f].TypeF)
{
case _alfafld:
case _memofld:
quoted_string(query, curr.get(fld));
break;
case _datefld:
if (drv == ODBC_mssql)
{
query << "CONVERT(datetime,";
quoted_string(query, curr.get(fld));
query << " ,105)";
}
else
if (drv == ODBC_mysql)
quoted_string(query, format("&ld", curr.get_date(fld)));
else
if (drv == ODBC_access)
{
query << "CDATE(";
quoted_string(query, curr.get(fld));
query << ")";
}
break;
case _realfld:
query << curr.get_real(fld).string(-1,2);
break;
case _boolfld:
query << (curr.get_bool(fld) ? '1' : '0');
break;
default:
query << curr.get_long(fld);
break;
}
}
query << ");";
TODBC_recordset upd("");
int err = 0;
if (upd.connect(dsn()))
err = upd.exec(query);
return err;
}
void TODBC_recordset::update_rec(const TISAM_recordset& dbfset)
{
const TRectype& curr = dbfset.cursor()->curr();
const int lf = curr.num();
TString table = prefix().get_filename(lf).name_only(); table.upper();
TString query; query << "UPDATE " << table << " SET \n";
TODBC_driver drv = driver();
const RecDes& recdes = curr.rec_des();
const unsigned int nfields = recdes.NFields;
for (unsigned int f = 0; f < nfields; f++)
{
const char* fld = recdes.Fd[f].Name;
if (f) query << ',';
query << fld << '=';
switch (recdes.Fd[f].TypeF)
{
case _alfafld:
case _memofld:
quoted_string(query, curr.get(fld));
break;
case _datefld:
if (drv == ODBC_mssql)
{
query << "CONVERT(datetime,";
quoted_string(query, curr.get(fld));
query << " ,105)";
}
else
if (drv == ODBC_mysql)
quoted_string(query, format("&ld", curr.get_date(fld)));
else
if (drv == ODBC_access)
{
query << "CDATE(";
quoted_string(query, curr.get(fld));
query << ")";
}
break;
case _realfld:
query << curr.get_real(fld).string(-1,2);
break;
case _boolfld:
query << (curr.get_bool(fld) ? '1' : '0');
break;
default:
query << curr.get_long(fld);
break;
}
}
query << "\nWHERE ";
const KeyDes& key = recdes.Ky[0];
for (int k = 0; k < key.NkFields; k++)
{
if (k > 0) query << " AND ";
const int nf = key.FieldSeq[k] % MaxFields;
const char* fname = recdes.Fd[nf].Name;
query << fname << '=';
if (recdes.Fd[nf].TypeF == _alfafld)
query << '\'' << curr.get(fname) << '\'';
else
query << curr.get(fname);
}
TODBC_recordset upd("");
if (upd.connect(dsn()))
upd.exec(query);
}
void TODBC_recordset::remove_rec(const TISAM_recordset& dbfset)
{
const TRectype& curr = dbfset.cursor()->curr();
const int lf = curr.num();
TString table = prefix().get_filename(lf).name_only(); table.upper();
TString query;
query << "DELETE FROM " << table << " WHERE ";
const RecDes& recdes = dbfset.cursor()->curr().rec_des();
const int keyno = dbfset.cursor()->key();
const KeyDes& keydes = recdes.Ky[keyno-1];
for (int f = 0; f < keydes.NkFields; f++)
{
if (f) query << " AND ";
const int nfield = keydes.FieldSeq[f] % MaxFields;
const char* fname = recdes.Fd[nfield].Name;
query << fname << '=';
switch (recdes.Fd[f].TypeF)
{
case _alfafld:
quoted_string(query, get(fname).as_string());
break;
case _realfld:
case _intfld:
case _longfld:
case _intzerofld:
case _longzerofld:
{
TString value = get(fname).as_string();
if (value.blank())
value = "0";
query << value;
}
break;
default:
query << get(fname);
break;
break;
}
}
query << ';';
TODBC_recordset upd("");
if (upd.connect(dsn()))
upd.exec(query);
}
void TODBC_recordset::set(const char* sql)
{
if (!_freezed || !_loaded || _sql != sql)
reset();
_sql = sql;
if (_sql.find("SELECT") >= 0 || _sql.find("select") >= 0)
find_and_reset_vars();
}
TODBC_recordset::TODBC_recordset(const char* sql, const bool freezed) : _freezed(freezed), _loaded(false)
{
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;
}