2016-09-09 13:58:28 +00:00
|
|
|
|
#include <diction.h>
|
|
|
|
|
#include <odbcrset.h>
|
|
|
|
|
#include <sqlset.h>
|
|
|
|
|
#include <textset.h>
|
|
|
|
|
#include <utility.h>
|
2019-03-12 17:38:18 +01:00
|
|
|
|
#include "tsdb.h"
|
2016-09-09 13:58:28 +00:00
|
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////
|
|
|
|
|
// 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
|
2019-01-15 10:10:28 +01:00
|
|
|
|
{
|
|
|
|
|
return _sql;
|
|
|
|
|
}
|
|
|
|
|
|
2016-09-09 13:58:28 +00:00
|
|
|
|
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()
|
|
|
|
|
{ }
|
|
|
|
|
|
2019-04-09 12:46:12 +02:00
|
|
|
|
///////////////////////////////////////////////////////////
|
|
|
|
|
// TDB_recordset
|
|
|
|
|
///////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
void TDB_recordset::reset()
|
|
|
|
|
{
|
|
|
|
|
_current_row = -1;
|
|
|
|
|
_is_loaded = false;
|
|
|
|
|
_items = 0;
|
|
|
|
|
_row.destroy();
|
|
|
|
|
_column.destroy();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* La query pu<70> iniziare con la stringa di connessione cos<6F> fatta:
|
|
|
|
|
* CONNECT(server, user, psw, driver = "MSSQL") il driver pu<EFBFBD> essere omesso.
|
|
|
|
|
* Se c'<EFBFBD> la estraggo, setto la connessione e prendo la vera query.
|
|
|
|
|
* Se no setto direttamente la query.
|
|
|
|
|
* Ritorno false se non mi sono mai connesso a niente e non passo
|
|
|
|
|
* la stringa di connessione.
|
|
|
|
|
*/
|
|
|
|
|
bool TDB_recordset::set(const char* sql)
|
|
|
|
|
{
|
|
|
|
|
bool ok;
|
|
|
|
|
TString real_query = "";
|
|
|
|
|
// Posso modificare oppure non posso ma _sql <20> vuota
|
|
|
|
|
if (!_freezed || _sql.empty())
|
|
|
|
|
{
|
|
|
|
|
if (_sql.empty() || !_freezed && !_sql.empty())
|
|
|
|
|
{
|
|
|
|
|
// Guardo se la query inizia con la stringa di connessione
|
|
|
|
|
if (TString(sql).starts_with("CONNECT(", true))
|
|
|
|
|
{
|
|
|
|
|
TString query(sql);
|
|
|
|
|
int pos_EOCon = query.find(')'); // End Of Conn
|
|
|
|
|
const TString& conn_str = query.sub(0, ++pos_EOCon);
|
|
|
|
|
ok = set_connection(conn_str);
|
|
|
|
|
if (!ok)
|
|
|
|
|
return false;
|
|
|
|
|
real_query << query.sub(pos_EOCon);
|
|
|
|
|
}
|
|
|
|
|
else if (!is_connected())
|
|
|
|
|
return false;
|
|
|
|
|
else
|
|
|
|
|
real_query << sql;
|
|
|
|
|
|
|
|
|
|
// Non sto facendo una select
|
|
|
|
|
if (real_query.find("SELECT") == -1 && real_query.find("select") == -1)
|
|
|
|
|
{
|
|
|
|
|
_sql.cut(0);
|
|
|
|
|
reset();
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Serve?
|
|
|
|
|
if (!_freezed || _sql != sql)
|
|
|
|
|
reset();
|
|
|
|
|
|
|
|
|
|
_sql.cut(0) << real_query;
|
|
|
|
|
if (_sql.find("SELECT") >= 0 || _sql.find("select") >= 0)
|
|
|
|
|
find_and_reset_vars();
|
|
|
|
|
|
|
|
|
|
bool is_set;
|
|
|
|
|
if ((is_set = _rec->sq_set(_sql))) {
|
|
|
|
|
unset_loaded();
|
|
|
|
|
}
|
|
|
|
|
return is_set;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Parso la stringa di connessione ed estraggo i dati.
|
|
|
|
|
* Se il numero di dati <EFBFBD> sufficiente eseguo la connessione.
|
|
|
|
|
* Ritorno false se non riesco a connettermi o il numero di dati <EFBFBD> sbagliato
|
|
|
|
|
*/
|
|
|
|
|
bool TDB_recordset::set_connection(const char* conn_str) const
|
|
|
|
|
{
|
|
|
|
|
TString pn(conn_str);
|
|
|
|
|
TString srv = "", usr = "", pwd = "", drv = "";
|
|
|
|
|
|
|
|
|
|
int first_pos = pn.find("(", 0);
|
|
|
|
|
int last_pos = pn.find(",", first_pos);
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
for (i = 0; last_pos != -1; i++) {
|
|
|
|
|
switch (i)
|
|
|
|
|
{
|
|
|
|
|
case 0:
|
|
|
|
|
srv = pn.sub(first_pos + 1, last_pos);
|
|
|
|
|
break;
|
|
|
|
|
case 1:
|
|
|
|
|
usr = pn.sub(first_pos + 1, last_pos);
|
|
|
|
|
break;
|
|
|
|
|
case 2:
|
|
|
|
|
pwd = pn.sub(first_pos + 1, last_pos);
|
|
|
|
|
break;
|
|
|
|
|
case 3:
|
|
|
|
|
drv = pn.sub(first_pos + 1, last_pos);
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
last_pos = -1;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
first_pos = last_pos;
|
|
|
|
|
last_pos = pn.find(",", first_pos + 1);
|
|
|
|
|
if (last_pos == -1)
|
|
|
|
|
last_pos = pn.find(")", first_pos + 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Guardo se ho valorizzato almeno i primi 3 elementi della connect
|
|
|
|
|
// Se non valorizzo l'ultimo come default: MSSQL Server
|
|
|
|
|
if (i == 3)
|
|
|
|
|
return connect(srv.ltrim(), usr.ltrim(), pwd.ltrim(), TSDB_MSSQL);
|
|
|
|
|
if (i == 4)
|
|
|
|
|
return connect(srv.ltrim(), usr.ltrim(), pwd.ltrim(), str_to_driver(drv.ltrim()));
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Ritorna true se si connette
|
|
|
|
|
bool TDB_recordset::connect(const char * db, const char * user, const char * pass, const TT_driver tipo_db) const
|
|
|
|
|
{
|
|
|
|
|
const bool connected = _rec->sq_connect(db, user, pass, tipo_db) == NOERR;
|
|
|
|
|
// Nel dubbio setto entrambi
|
|
|
|
|
_rec->sq_set_con_option("UseDynamicCursor", "True");
|
|
|
|
|
_rec->sq_set_con_option("Scrollable", "True");
|
|
|
|
|
return connected;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TT_driver TDB_recordset::str_to_driver(const char* tipo_db)
|
|
|
|
|
{
|
|
|
|
|
if (_stricmp(tipo_db, "") == 0)
|
|
|
|
|
return TSDB_undefined;
|
|
|
|
|
//! ODBC
|
|
|
|
|
if (_stricmp(tipo_db, "ODBC") == 0)
|
|
|
|
|
return TSDB_ODBC;
|
|
|
|
|
//! Oracle
|
|
|
|
|
if (_stricmp(tipo_db, "Oracle") == 0)
|
|
|
|
|
return TSDB_Oracle;
|
|
|
|
|
//! Microsoft SQL Server
|
|
|
|
|
if (_stricmp(tipo_db, "MSSQL") == 0)
|
|
|
|
|
return TSDB_MSSQL;
|
|
|
|
|
//! InterBase or Firebird
|
|
|
|
|
if (_stricmp(tipo_db, "InterBase") == 0)
|
|
|
|
|
return TSDB_InterBase;
|
|
|
|
|
//! SQLBase
|
|
|
|
|
if (_stricmp(tipo_db, "SQLBase") == 0)
|
|
|
|
|
return TSDB_SQLBase;
|
|
|
|
|
//! IBM DB2
|
|
|
|
|
if (_stricmp(tipo_db, "DB2") == 0)
|
|
|
|
|
return TSDB_DB2;
|
|
|
|
|
//! Informix
|
|
|
|
|
if (_stricmp(tipo_db, "Informix") == 0)
|
|
|
|
|
return TSDB_Informix;
|
|
|
|
|
//! Sybase ASE
|
|
|
|
|
if (_stricmp(tipo_db, "Sybase") == 0)
|
|
|
|
|
return TSDB_Sybase;
|
|
|
|
|
//! MySQL
|
|
|
|
|
if (_stricmp(tipo_db, "MySQL") == 0)
|
|
|
|
|
return TSDB_MySQL;
|
|
|
|
|
//! PostgreSQL
|
|
|
|
|
if (_stricmp(tipo_db, "PostgreSQL") == 0)
|
|
|
|
|
return TSDB_PostgreSQL;
|
|
|
|
|
//! SQLite
|
|
|
|
|
if (_stricmp(tipo_db, "SQLite") == 0)
|
|
|
|
|
return TSDB_SQLite;
|
|
|
|
|
//! SQL Anywere
|
|
|
|
|
if (_stricmp(tipo_db, "SQLAnywhere") == 0)
|
|
|
|
|
return TSDB_SQLAnywhere;
|
|
|
|
|
return TSDB_undefined;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool TDB_recordset::set_loaded()
|
|
|
|
|
{
|
|
|
|
|
bool ok = false;
|
|
|
|
|
if (!_sql.empty() && _rec->sq_exec(false))
|
|
|
|
|
{
|
|
|
|
|
ok = _is_loaded = true;
|
|
|
|
|
_items = _rec->sq_items();
|
|
|
|
|
_ncolumns = _rec->sq_get_num_fields();
|
|
|
|
|
}
|
|
|
|
|
return ok;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void TDB_recordset::unset_loaded()
|
|
|
|
|
{
|
|
|
|
|
_is_loaded = false;
|
|
|
|
|
_items = 0;
|
|
|
|
|
_ncolumns = 0;
|
|
|
|
|
_current_row = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void TDB_recordset::freeze(const bool on)
|
|
|
|
|
{
|
|
|
|
|
if (on)
|
|
|
|
|
_rec->freeze();
|
|
|
|
|
else
|
|
|
|
|
_rec->defrost();
|
|
|
|
|
_freezed = on;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool TDB_recordset::connect(const char* db, const char* user, const char* pass, const char* tipo_db) const
|
|
|
|
|
{
|
|
|
|
|
return _rec->sq_connect(db, user, pass, str_to_driver(tipo_db)) == NOERR;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TRecnotype TDB_recordset::items() const
|
|
|
|
|
{
|
|
|
|
|
if (_is_loaded)
|
|
|
|
|
return _items;
|
|
|
|
|
return _rec->sq_items();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool TDB_recordset::move_to(TRecnotype pos)
|
|
|
|
|
{
|
|
|
|
|
const TRecnotype tot = items();
|
|
|
|
|
TRecnotype row = pos;
|
|
|
|
|
bool ok = true;
|
|
|
|
|
|
|
|
|
|
if (pos < 0)
|
|
|
|
|
row = 0;
|
|
|
|
|
if (pos > tot)
|
|
|
|
|
row = tot;
|
|
|
|
|
|
|
|
|
|
if (!_is_loaded)
|
|
|
|
|
ok = set_loaded();
|
|
|
|
|
|
|
|
|
|
if (ok && ((ok = _rec->sq_go(row))))
|
|
|
|
|
_current_row = pos;
|
|
|
|
|
|
|
|
|
|
return ok;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool TDB_recordset::move_next()
|
|
|
|
|
{
|
|
|
|
|
bool ok = true;
|
|
|
|
|
|
|
|
|
|
if (!_is_loaded)
|
|
|
|
|
ok = set_loaded();
|
|
|
|
|
|
|
|
|
|
if (ok && _rec->sq_next())
|
|
|
|
|
{
|
|
|
|
|
_current_row = _rec->sq_pos();
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const TString_array TDB_recordset::get_next_row()
|
|
|
|
|
{
|
|
|
|
|
if (move_next())
|
|
|
|
|
return get_row();
|
|
|
|
|
return TString_array();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const TString_array TDB_recordset::get_row(TRecnotype n)
|
|
|
|
|
{
|
|
|
|
|
bool ok = true;
|
|
|
|
|
|
|
|
|
|
// Get della riga attuale
|
|
|
|
|
if (n == -1)
|
|
|
|
|
n = current_row();
|
|
|
|
|
|
|
|
|
|
else if (_current_row != n)
|
|
|
|
|
ok = move_to(n); // Solo se non sono gi<67> su quella riga
|
|
|
|
|
|
|
|
|
|
if (ok)
|
|
|
|
|
{
|
|
|
|
|
const unsigned ncol = _rec->sq_get_num_fields();
|
|
|
|
|
_row.destroy();
|
|
|
|
|
for (unsigned i = 0; i < ncol; i++)
|
|
|
|
|
_row.add(TString(_rec->sq_get(i, false)));
|
|
|
|
|
return _row;
|
|
|
|
|
}
|
|
|
|
|
// else
|
|
|
|
|
_row.destroy();
|
|
|
|
|
return _row;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void TDB_recordset::requery()
|
|
|
|
|
{
|
|
|
|
|
_items = 0;
|
|
|
|
|
_current_row = -1;
|
|
|
|
|
_row.destroy();
|
|
|
|
|
_column.destroy();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
unsigned TDB_recordset::columns() const
|
|
|
|
|
{
|
|
|
|
|
if (!_is_loaded)
|
|
|
|
|
{
|
|
|
|
|
TDB_recordset* my_self = const_cast<TDB_recordset*>(this);
|
|
|
|
|
my_self->set_loaded();
|
|
|
|
|
}
|
|
|
|
|
return _ncolumns;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const TRecordset_column_info& TDB_recordset::column_info(const unsigned column) const
|
|
|
|
|
{
|
|
|
|
|
static TRecordset_column_info info;
|
|
|
|
|
if (_is_loaded)
|
|
|
|
|
{
|
|
|
|
|
info._name = _rec->sq_get_name_field(column); // TString
|
|
|
|
|
info._width = _rec->sq_get_width_field(column); // int
|
|
|
|
|
info._type = _rec->sq_get_type_field(column); // TFieldtypes
|
|
|
|
|
info._pos = column;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
info._name.cut(0); // TString
|
|
|
|
|
info._width = 0; // int
|
|
|
|
|
info._type = _alfafld; // TFieldtypes
|
|
|
|
|
info._pos = 0;
|
|
|
|
|
}
|
|
|
|
|
return info;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const TVariant& TDB_recordset::get(unsigned int column) const
|
|
|
|
|
{
|
|
|
|
|
static TVariant field = NULL_VARIANT;
|
|
|
|
|
static unsigned int last_get = 0;
|
|
|
|
|
if (!_freezed || column != last_get || field == NULL_VARIANT)
|
|
|
|
|
{
|
|
|
|
|
last_get = column;
|
|
|
|
|
field = _rec->sq_get(column);
|
|
|
|
|
}
|
|
|
|
|
return field;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const TVariant& TDB_recordset::get(const char* name) const
|
|
|
|
|
{
|
|
|
|
|
return TRecordset::get(name);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool TDB_recordset::is_connected() const
|
|
|
|
|
{
|
|
|
|
|
return _rec->sq_is_connect();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const TVariant& TDB_recordset::active_connection() const
|
|
|
|
|
{
|
|
|
|
|
static TVariant conn = NULL_VARIANT;
|
|
|
|
|
conn.add(_dsn);
|
|
|
|
|
conn.add(_usr);
|
|
|
|
|
conn.add(_drv);
|
|
|
|
|
return conn;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool TDB_recordset::exec(const char* sql)
|
|
|
|
|
{
|
|
|
|
|
bool exec = false;
|
|
|
|
|
if ((exec = _rec->sq_set_exec(sql))) {
|
|
|
|
|
_is_loaded = true;
|
|
|
|
|
_items = _rec->sq_items();
|
|
|
|
|
}
|
|
|
|
|
return exec;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool TDB_recordset::commit() const
|
|
|
|
|
{
|
|
|
|
|
return _rec->sq_commit();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TDB_recordset::TDB_recordset(const char* sql, const bool freezed) : _freezed(freezed)
|
|
|
|
|
{
|
|
|
|
|
_current_row = -1;
|
|
|
|
|
_rec = new SSimple_query();
|
|
|
|
|
//_rec->sq_set_autocommit(true);
|
|
|
|
|
_sql.cut(0);
|
|
|
|
|
_is_loaded = false;
|
|
|
|
|
_items = 0;
|
|
|
|
|
_ncolumns = 0;
|
|
|
|
|
freeze(freezed);
|
|
|
|
|
set(sql);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TDB_recordset::~TDB_recordset()
|
|
|
|
|
{
|
|
|
|
|
delete _rec;
|
|
|
|
|
}
|
|
|
|
|
|
2016-09-09 13:58:28 +00:00
|
|
|
|
///////////////////////////////////////////////////////////
|
|
|
|
|
// 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))
|
2019-03-25 11:54:02 +01:00
|
|
|
|
rex = new TAS400_recordset(sql); else
|
2019-03-15 10:51:50 +01:00
|
|
|
|
if (sql.starts_with("CONNECT", true))
|
|
|
|
|
rex = new TDB_recordset(sql);
|
2016-09-09 13:58:28 +00:00
|
|
|
|
else
|
|
|
|
|
rex = new TSQL_recordset(sql);
|
|
|
|
|
}
|
|
|
|
|
return rex;
|
|
|
|
|
}
|