campo-sirio/ba/ba8201.cpp

1213 lines
26 KiB
C++
Raw Normal View History

#ifdef WIN32
#include <fstream.h>
#else
#include "../xvaga/incstr.h"
#endif
#include <diction.h>
#include <extcdecl.h>
#include <progind.h>
#include <relation.h>
#include <utility.h>
#include <xml.h>
#include "ba8201.h"
///////////////////////////////////////////////////////////
// TVariant
///////////////////////////////////////////////////////////
static const TVariant NULL_VARIANT;
void TVariant::set_null()
{
if (_ptr != NULL)
{
if (_type != _nullfld && _type != _longfld)
delete _ptr;
_ptr = NULL;
}
_type = _nullfld;
}
void TVariant::set(const char* str)
{
if (_type == _alfafld)
*((TString*)_ptr) = str;
else
{
set_null();
_type = _alfafld;
_ptr = new TString(str);
}
}
void TVariant::set(const real& r)
{
if (_type == _realfld)
*((real*)_ptr) = r;
else
{
set_null();
_type = _realfld;
_ptr = new real(r);
}
}
void TVariant::set(const TDate& d)
{
if (_type == _datefld)
*((TDate*)_ptr) = d;
else
{
set_null();
_type = _datefld;
_ptr = new TDate(d);
}
}
void TVariant::set(const long n)
{
if (_type != _longfld)
set_null();
_type = _longfld;
_ptr = (void*)n;
}
TDate TVariant::as_date() const
{
if (_type == _datefld)
return *(TDate*)_ptr;
const TDate d(as_int());
return d;
}
long TVariant::as_int() const
{
long n = 0;
switch(_type)
{
case _datefld: n = as_date().date2ansi(); break;
case _longfld: n = (long)_ptr; break;
case _realfld: n = as_real().integer(); break;
case _alfafld: n = atoi(as_string()); break;
default : break;
}
return n;
}
bool TVariant::as_bool() const
{
bool ok = false;
if (_type == _alfafld)
ok = strchr("XY", as_string()[0]) != NULL;
else
ok = as_int() != 0;
return ok;
}
COLOR TVariant::as_color() const
{
const unsigned long rgb = as_int();
unsigned char r = XVT_COLOR_GET_RED(rgb);
unsigned char g = XVT_COLOR_GET_GREEN(rgb);
unsigned char b = XVT_COLOR_GET_BLUE(rgb);
return MAKE_COLOR(r, g, b);
}
real TVariant::as_real() const
{
if (_type == _realfld)
return *(real*)_ptr;
switch(_type)
{
case _alfafld: return real(as_string()); break;
case _longfld: return real(as_int()); break;
default : break;
}
return ZERO;
}
bool TVariant::as_string(TString& tmp) const
{
tmp.cut(0);
switch(_type)
{
case _alfafld: tmp = *(TString*)_ptr; break;
case _datefld: tmp = as_date().string(); break;
case _longfld: tmp << as_int(); break;
case _realfld: tmp = as_real().string(); break;
default: break;
}
return !is_null();
}
const TString& TVariant::as_string() const
{
if (_type == _alfafld)
return *(TString*)_ptr;
TString& tmp = get_tmp_string();
as_string(tmp);
return tmp;
}
void TVariant::convert_to(TFieldtypes ft)
{
if (_type != ft)
{
switch (ft)
{
case _alfafld: set(as_string()); break;
case _datefld: set(as_date()); break;
case _longfld: set(as_int()); break;
case _realfld: set(as_real()); break;
default : set_null(); break;
}
}
}
void TVariant::copy(const TVariant& var)
{
switch (var._type)
{
case _datefld: set(var.as_date()); break;
case _longfld: set(var.as_int()); break;
case _realfld: set(var.as_real()); break;
case _alfafld: set(var.as_string()); break;
default : set_null(); break;
}
}
int TVariant::compare(const TSortable& s) const
{
const TVariant& var = (const TVariant&)s;
int cmp = 0;
switch (_type)
{
case _datefld: cmp = as_date() - var.as_date(); break;
case _longfld: cmp = as_int() - var.as_int(); break;
case _realfld:
{
const real n = as_real() - var.as_real();
cmp = n.sign();
}
break;
case _alfafld: cmp = as_string().compare(var.as_string()); break;
default : cmp = var.is_null() ? 0 : -1;
}
return cmp;
}
TVariant& TVariant::add(const TVariant& var)
{
switch (_type)
{
case _datefld: set(as_date() + var.as_int()); break;
case _longfld: set(as_int() + var.as_int()); break;
case _alfafld: *(TString*)_ptr << var.as_string(); break;
case _realfld: *(real*)_ptr += var.as_real(); break;
default: copy(var); break;
}
return *this;
}
TVariant& TVariant::sub(const TVariant& var)
{
switch (_type)
{
case _datefld: set(as_date() - var.as_int()); break;
case _longfld:
if (var.type() == _longfld)
{
set(as_int() - var.as_int());
break;
}
// Fall down
default:
{
real n = as_real();
n -= var.as_real();
set(n);
}
break;
}
return *this;
}
///////////////////////////////////////////////////////////
// Utility
///////////////////////////////////////////////////////////
static bool is_numeric(const char* str)
{
if (str == NULL || *str == '\0' || *str == '0')
return false; // Se comincia per zero va preservato!
if (*str == '-')
{
str++;
if (*str <= ' ')
return false;
}
while (*str)
{
if (strchr("0123456789.", *str) == NULL)
return false;
str++;
}
return true;
}
///////////////////////////////////////////////////////////
// TRecordset
///////////////////////////////////////////////////////////
const TString& TRecordset::query_text() const
{
return EMPTY_STRING;
}
const TToken_string& TRecordset::sheet_head() const
{
TToken_string& head = get_tmp_string();
TToken_string tablefield(32, '.');
for (unsigned int c = 0; c < columns(); c++)
{
const TRecordset_column_info& ci = column_info(c);
tablefield = ci._name;
int maxlen = 0;
FOR_EACH_TOKEN(tablefield, tok)
{
if (maxlen == 0)
head.add(tok);
else
head << '\n' << tok;
const int len = strlen(tok);
if (len > maxlen)
maxlen = len;
}
head << '@' << max(ci._width, maxlen);
if (ci._type == _longfld || ci._type == _realfld)
head << 'R';
}
return head;
}
bool TRecordset::save_as_html(const char* path)
{
TProgind pi(items(), TR("Esportazione in corso..."), true, true);
ofstream out(path);
out << "<html>" << endl;
out << "<body>" << endl;
out << "<table border=\"1\">" << endl;
out << " <caption><h1>" << path << "</h1></caption>" << endl;
out << " <thead>";
for (unsigned int c = 0; c < columns(); c++)
{
const TRecordset_column_info& ci = column_info(c);
TToken_string header(ci._name, '.');
TString str;
FOR_EACH_TOKEN(header, tok)
{
if (str.not_empty())
str << "<br/>";
str << tok;
}
out << " <th>" << str << "</th>" << endl;
}
out << " </thead>" << endl;
TString val;
for (TRecnotype n = 0; n < items(); n++)
{
move_to(n);
pi.addstatus(1);
if (pi.iscancelled())
break;
out << " <tr>" << endl;
for (unsigned int c = 0; c < columns(); c++)
{
const TRecordset_column_info& ci = column_info(c);
out << " <td";
if (ci._type == _longfld || ci._type == _realfld)
out << " align=\"right\"";
out << ">";
get(c).as_string(val);
if (!val.blank())
{
val.rtrim();
out << val;
}
out << " </td>" << endl;
}
out << " </tr>" << endl;
}
out << "</table>" << endl;
out << "</body>" << endl;
out << "</html>" << endl;
return !pi.iscancelled();
}
bool TRecordset::save_as_silk(const char* path)
{
TProgind pi(items(), TR("Esportazione in corso..."), true, true);
ofstream out(path);
out << "ID;PWXL;N;E" << endl;
for (unsigned int c = 0; c < columns(); c++)
{
const TRecordset_column_info& ci = column_info(c);
out << "C;Y1;X" << (c+1) << ";K\"" << ci._name << '"' << endl;
}
TString val;
for (TRecnotype n = 0; n < items(); n++)
{
move_to(n);
pi.addstatus(1);
if (pi.iscancelled())
break;
for (unsigned int c = 0; c < columns(); c++)
{
out << "C;Y" << (n+2) << ";X" << (c+1) << ";K\"";
get(c).as_string(val);
if (!val.blank())
{
val.rtrim();
val.replace('"', '\'');
out << val;
}
out << '"' << endl;
}
}
out << "E" << endl;
return !pi.iscancelled();
}
bool TRecordset::save_as_text(const char* path)
{
TProgind pi(items(), TR("Esportazione in corso..."), true, true);
ofstream out(path);
TString val;
for (TRecnotype n = 0; n < items(); n++)
{
move_to(n);
for (unsigned int c = 0; c < columns(); c++)
{
if (c > 0)
out << '\t';
get(c).as_string(val);
if (!val.blank())
{
val.rtrim();
out << val;
}
}
out << endl;
pi.addstatus(1);
if (pi.iscancelled())
break;
}
return !pi.iscancelled();
}
bool TRecordset::save_as_campo(const char* path)
{
TProgind pi(items(), TR("Esportazione in corso..."), true, true);
ofstream out(path);
out << "[Head]" << endl;
out << "Version=0";
for (unsigned int c = 0; c < columns(); c++)
{
const TRecordset_column_info& ci = column_info(c);
if ((c % 8) == 0)
out << endl << "Fields=";
else
out << '|';
out << ci._name;
}
out << endl << endl << "[Data]" << endl;
TString val;
for (TRecnotype n = 0; n < items(); n++)
{
move_to(n);
for (unsigned int c = 0; c < columns(); c++)
{
if (c > 0)
out << '|';
get(c).as_string(val);
if (!val.blank())
{
val.rtrim();
out << val;
}
}
out << endl;
pi.addstatus(1);
if (pi.iscancelled())
break;
}
return !pi.iscancelled();
}
bool TRecordset::save_as(const char* path, TRecordsetExportFormat fmt)
{
if (fmt == fmt_unknown)
{
TString ext;
xvt_fsys_parse_pathname(path, NULL, NULL, NULL, ext.get_buffer(), NULL);
ext.lower();
if (ext.starts_with("htm"))
fmt = fmt_html; else
if (ext == "xls" || ext == "slk")
fmt = fmt_silk;
}
bool ok = false;
switch (fmt)
{
case fmt_html : ok = save_as_html(path); break;
case fmt_silk : ok = save_as_silk(path); break;
case fmt_campo: ok = save_as_campo(path); break;
default : ok = save_as_text(path); break;
}
return ok;
}
const TVariant& TRecordset::get(const char* column_name) const
{
if (*column_name == '#' && variables().items() > 0)
return get_var(column_name);
for (unsigned int i = 0; i < columns(); i++)
{
const TRecordset_column_info& info = column_info(i);
if (info._name == column_name)
return get(i);
}
return NULL_VARIANT;
}
const TVariant& TRecordset::get_var(const char* name) const
{
const TVariant* var = (const TVariant*)_var.objptr(name);
return var != NULL ? *var : NULL_VARIANT;
}
bool TRecordset::set_var(const char* name, const TVariant& var, bool create)
{
bool ok = false;
TVariant* old = (TVariant*)_var.objptr(name);
if (old != NULL)
{
*old = var;
ok = true;
}
else
{
if (create)
{
_var.add(name, var);
_varnames.add(name);
ok = true;
}
}
return ok;
}
bool ask_variable(const char* name, TVariant& var)
{
TMask m("Richiesta variabile", 1, 52, 4);
m.add_static(-1, 0, name, 1, 0);
m.add_string(101, 0, "", 1, 1, 80, "", 50);
m.add_button(DLG_OK, 0, "", -12, -1, 10, 2);
m.add_button(DLG_CANCEL, 0, "", -22, -1, 10, 2);
m.set(101, var.as_string());
const bool ok = m.run() == K_ENTER;
if (ok)
{
const TString& str = m.get(101);
if (is_numeric(str))
var = real(str);
else
var = str;
}
return ok;
}
bool TRecordset::ask_variables(bool all) const
{
const bool ok = variables().items() > 0;
if (ok) // Se ci sono variabili faccio le sostituzioni
{
FOR_EACH_ARRAY_ROW(_varnames, i, name)
{
TVariant var = get_var(*name);
if (var.is_null() || all) // Mi serve assolutamente un valore!
{
ask_variable(*name, var);
((TSQL_recordset*)this)->set_var(*name, var);
}
}
}
return ok;
}
///////////////////////////////////////////////////////////
// Utility
///////////////////////////////////////////////////////////
bool select_custom_file(TFilename& path, const char* ext)
{
TFilename custom = firm2dir(-1);
custom.add("custom");
if (!custom.exist())
xvt_fsys_mkdir(custom);
path = custom;
path.add("*");
path.ext(ext);
TString_array files;
list_files(path, files);
TArray_sheet sheet(-1, -1, 78, 20, TR("Selezione"), TR("Nome@20|Descrizione@50"));
TString str;
FOR_EACH_ARRAY_ROW(files, i, row)
{
TXmlItem item;
if (item.Load(*row))
{
TToken_string* riga = new TToken_string;
path = *row;
path = path.name();
path.ext("");
riga->add(path);
const TXmlItem* desc = item.FindFirst("description");
str = *row;
if (desc != NULL)
desc->GetEnclosedText(str);
riga->add(str);
sheet.add(riga);
}
}
const bool ok = sheet.run() == K_ENTER;
if (ok)
{
path = custom;
path.add(sheet.row(-1).get(0));
path.ext(ext);
}
return ok;
}
///////////////////////////////////////////////////////////
// Private interface
///////////////////////////////////////////////////////////
#include "../sqlite/sqlite.h"
class TSQLite : public TObject
{
sqlite* _handle;
TFilename _currdb;
TAssoc_array _names;
protected:
void logicnum2name(int logicnum, TString& name) const;
int name2logicnum(const TString& name) const;
TVariant& get_sql_value(const TRectype& curr, const RecFieldDes& fd, TVariant& tmp) const;
void build_curr_path(TFilename& name) const;
void test_path();
bool esporta(const TRectype& rec, ostream& sql) const;
bool create_dbf_times();
long get_dbf_time(const TString& table);
bool set_dbf_time(const TString& table, long last);
bool import(int logicnum);
public:
sqlite* open(const char* fname = NULL);
bool exec(const char* sql, sqlite_callback callback = NULL, void* jolly = NULL, bool show_error = true);
void close();
bool exists(const char* table);
bool parse_select_from(const char* szSql);
TSQLite();
virtual ~TSQLite();
} _TheDataBase;
void get_sql_directory(TFilename& name)
{
name = firm2dir(-1);
name.add("sql");
if (!name.exist())
make_dir(name);
}
void TSQLite::build_curr_path(TFilename& name) const
{
TString16 firm; firm.format("%05ldA.sql", prefix().get_codditta());
get_sql_directory(name);
name.add(firm);
}
sqlite* TSQLite::open(const char* fname)
{
close();
_currdb = fname;
char* errmsg = NULL;
_handle = sqlite_open(_currdb, 0, &errmsg);
if (errmsg != NULL)
{
error_box(errmsg);
sqlite_freemem(errmsg);
}
create_dbf_times();
return _handle;
}
void TSQLite::test_path()
{
TFilename n;
build_curr_path(n);
if (n != _currdb)
open(n);
}
bool TSQLite::exec(const char* sql, sqlite_callback callback, void* jolly, bool show_error)
{
if (_handle == NULL)
test_path();
TWait_cursor hourglass;
char* errmsg = NULL;
const int rc = sqlite_exec(_handle, sql, callback, jolly, &errmsg);
if (errmsg != NULL)
{
if (show_error)
{
TString msg;
msg << sql;
msg.cut(128);
msg << '\n' << errmsg;
error_box(msg);
}
sqlite_freemem(errmsg);
}
return rc == SQLITE_OK;
}
void TSQLite::close()
{
if (_handle != NULL)
{
sqlite_close(_handle);
_handle = NULL;
}
}
const char* const DBF_TIMES_TABLE = "DBF_TIMES";
bool TSQLite::create_dbf_times()
{
bool ok = exists(DBF_TIMES_TABLE);
if (!ok)
{
TString sql;
sql << "CREATE TABLE " << DBF_TIMES_TABLE << " (name,time);\n"
<< "CREATE UNIQUE INDEX " << DBF_TIMES_TABLE << "_1 ON " << DBF_TIMES_TABLE << " (name);";
ok = exec(sql);
}
return ok;
}
bool TSQLite::set_dbf_time(const TString& table, long last)
{
TString sql;
// sql << "INSERT OR REPLACE INTO " << DBF_TIMES_TABLE << " VALUES("
sql << "REPLACE INTO " << DBF_TIMES_TABLE << " VALUES("
<< '\'' << table << "','" << last << "');";
return exec(sql);
}
static int dbf_time_callback(void* jolly, int argc, char** argv, char** columns)
{
long& last = *(long*)jolly;
last = atol(argv[0]);
return SQLITE_OK;
}
long TSQLite::get_dbf_time(const TString& table)
{
TString sql;
sql << "SELECT time FROM " << DBF_TIMES_TABLE << " WHERE name='" << table << "';";
long last = 0;
exec(sql, dbf_time_callback, &last);
return last;
}
void TSQLite::logicnum2name(int logicnum, TString& name) const
{
const FileDes& fd = prefix().get_filedes(logicnum);
const TFilename n = fd.SysName;
name = n.name(); name.upper();
}
int TSQLite::name2logicnum(const TString& name) const
{
TString* cod = (TString*)_names.objptr(name);
if (cod == NULL)
{
FileDes dir;
CGetFile(LF_DIR, &dir, _nolock, NORDIR);
const int nfiles = (int)dir.EOD;
TString8 n;
for (int logic = LF_USER; logic < nfiles; logic++)
{
logicnum2name(logic, n);
cod = new TString4; cod->format("%d", logic);
((TAssoc_array&)_names).add(n, cod);
}
cod = (TString*)_names.objptr(name);
if (cod == NULL) // Continua ostinatamente a non esistere?
return 0;
}
return atoi(*cod);
}
TVariant& TSQLite::get_sql_value(const TRectype& curr, const RecFieldDes& fd, TVariant& tmp) const
{
switch (fd.TypeF)
{
case _realfld : tmp.set(curr.get_real(fd.Name)); break;
case _intfld :
case _longfld :
case _wordfld :
case _intzerofld :
case _longzerofld: tmp.set(curr.get_long(fd.Name)); break;
case _datefld : tmp.set(curr.get_date(fd.Name)); break;
case _boolfld : tmp.set(curr.get_bool(fd.Name)); break;
default : tmp.set(curr.get(fd.Name)); break;
}
return tmp;
}
static int exists_callback(void *jolly, int argc, char **argv, char **azColName)
{
bool& yes = *(bool*)jolly;
yes = argc > 0;
return SQLITE_OK;
}
bool TSQLite::exists(const char* table)
{
TString sql;
sql << "SELECT name FROM sqlite_master WHERE (type='table')AND(name='" << table << "');";
bool yes = false;
exec(sql, exists_callback, &yes, false);
return yes;
}
bool TSQLite::esporta(const TRectype& rec, ostream& sql) const
{
const RecDes& rd = *rec.rec_des();
TVariant tmp;
for (int i = 0; i < rd.NFields; i++)
{
if (i > 0) sql << '\t';
if (rd.Fd[i].TypeF == _memofld)
sql << "\\N";
else
{
get_sql_value(rec, rd.Fd[i], tmp);
sql << tmp.as_string();
}
}
sql << '\n';
return true;
}
bool TSQLite::import(int logicnum)
{
TString table; logicnum2name(logicnum, table);
long last = get_dbf_time(table);
if (logicnum >= LF_USER) // Dummy test
{
TLocalisamfile file(logicnum);
if (!file.is_changed_since(last))
return true;
}
const RecDes& rd = prefix().get_recdes(logicnum);
TString sql;
if (exists(table))
{
// Drop old table
sql.cut(0) << "DROP TABLE "<< table << ';';
exec(sql);
}
// Create new table
sql.cut(0) << "CREATE TABLE "<< table << "\n(";
for (int i = 0; i < rd.NFields; i++)
{
if (i > 0) sql << ',';
sql << rd.Fd[i].Name;
}
sql << ");";
if (!exec(sql))
return false;
// Create main index
// sql.cut(0) << "CREATE UNIQUE INDEX " << table << "_1 ON "<< table << "\n(";
sql.cut(0) << "CREATE INDEX " << table << "_1 ON "<< table << "\n(";
const KeyDes& kd = rd.Ky[0];
for (int k = 0; k < kd.NkFields; k++)
{
if (k > 0) sql << ',';
const int ndx = kd.FieldSeq[k] % MaxFields;
sql << rd.Fd[ndx].Name;
}
sql << ");";
if (!exec(sql))
return false;
TRelation rel(logicnum);
TCursor cur(&rel);
const TRecnotype items = cur.items();
cur.freeze();
const TRectype& curr = rel.curr();
TString msg;
msg << TR("Esportazione tabella") << ' ' << table;
msg << ": " << items << ' ' << TR("righe");
TProgind pi(items, msg, true, true);
TFilename tmp; tmp.tempdir(); tmp.add("sql.txt");
ofstream txt(tmp, ios::binary);
for (cur = 0; cur.pos() < items; ++cur)
{
esporta(curr, txt);
pi.addstatus(1);
if (pi.iscancelled())
break;
}
txt.close();
msg << '\n' << TR("Importazione tabella") << ' ' << table;
msg << ": " << items << ' ' << TR("righe");
pi.set_text(msg);
sql.cut(0) << "COPY " << table << " FROM '" << tmp << "';";
if (exec(sql))
set_dbf_time(table, last); // Aggiorna ora di ultima modifica
::remove(tmp);
return true;
}
bool is_var_separator(char c)
{
if (isspace(c))
return true;
return strchr("<=>,", c) != NULL;
}
bool TSQLite::parse_select_from(const char* szSql)
{
test_path();
TString sql(szSql);
sql.trim(); sql.upper();
if (!sql.starts_with("SELECT"))
return false;
const int from = sql.find("FROM");
if (from < 0)
return false;
const int where = sql.find("WHERE", from);
TToken_string tables(sql.sub(from+5, where), ',');
TString table;
FOR_EACH_TOKEN(tables, tok)
{
table = tok;
table.trim();
for (int i = 0; table[i]; i++)
{
if (table[i] <= ' ' || table[i] == ';')
{ table.cut(i); break; }
}
const int logicnum = name2logicnum(table);
if (logicnum > 0)
import(logicnum);
}
return true;
}
TSQLite::TSQLite() : _handle(NULL)
{
}
TSQLite::~TSQLite()
{
close();
}
///////////////////////////////////////////////////////////
// TSQL_recordset
///////////////////////////////////////////////////////////
void TSQL_recordset::reset()
{
_firstrow = _items = 0;
_pagesize = 512;
_page.destroy();
_column.destroy();
}
int TSQL_recordset::on_get_items(int argc, char** values, char** columns)
{
if (_column.items() == 0)
{
for (int i = 0; i < argc; i++)
{
TRecordset_column_info* info = new TRecordset_column_info;
info->_name = columns[i];
info->_width = 1;
info->_type = _realfld;
_column.add(info);
}
}
if (_items < _pagesize)
{
for (int i = 0; i < argc; i++) if (values[i] && *values[i])
{
TRecordset_column_info& info = (TRecordset_column_info&)_column[i];
const int len = strlen(values[i]);
if (len > info._width)
info._width = len;
if (info._type != _alfafld && isalpha(*values[i]))
info._type = _alfafld;
}
}
_items++;
return SQLITE_OK;
}
static int query_get_items(void* jolly, int argc, char** values, char** columns)
{
TSQL_recordset* q = (TSQL_recordset*)jolly;
q->on_get_items(argc, values, columns);
return SQLITE_OK;
}
void TSQL_recordset::parsed_sql_text(TString& sql) const
{
sql = _sql;
if (ask_variables(false)) // Se ci sono variabili faccio le sostituzioni
{
const TString_array& names = variables();
FOR_EACH_ARRAY_ROW(names, i, name) // Scandisco tutte le variabili
{
TVariant var = get_var(*name);
int pos = sql.find(*name);
for ( ; pos > 0; pos = sql.find(*name, pos+1))
{
const TString& after = sql.mid(pos+name->len());
sql.cut(pos);
TString s = var.as_string();
if ((var.type() == _alfafld && s[0] != '\'') || var.is_null())
{
s.insert("'");
s << '\'';
}
sql << s << after;
}
}
}
}
TRecnotype TSQL_recordset::items() const
{
if (_items == 0)
{
TString sql; parsed_sql_text(sql);
_TheDataBase.exec(sql, query_get_items, (TSQL_recordset*)this);
}
return _items;
}
unsigned int TSQL_recordset::columns() const
{
if (_column.items() == 0)
items();
return _column.items();
}
const TRecordset_column_info& TSQL_recordset::column_info(unsigned int c) const
{
return (const TRecordset_column_info&)_column[c];
}
static int query_get_rows(void* jolly, int argc, char** values, char** columns)
{
TArray& arr = *(TArray*)jolly;
TArray* a = new TArray;
for (int c = 0; c < argc; c++)
{
TVariant* var = new TVariant;
if (is_numeric(values[c]))
var->set(real(values[c]));
else
var->set(values[c]);
a->add(var, c);
}
arr.add(a);
return SQLITE_OK;
}
bool TSQL_recordset::move_to(TRecnotype n)
{
_current_row = n;
if (n < 0 || n >= items())
return false;
if (n < _firstrow || n >= _firstrow+_page.items())
{
TString sql; parsed_sql_text(sql);
if (sql.find("LIMIT ") < 0)
{
const int semicolon = sql.rfind(';');
if (semicolon >= 0)
sql.cut(semicolon);
sql.trim();
_page.destroy();
if (n >= _pagesize)
_firstrow = n-_pagesize/8; // Prendo qualche riga dalla pagina precedente, per velocizzare il pagina su
else
_firstrow = n;
sql << "\nLIMIT " << _pagesize << " OFFSET " << _firstrow << ';';
}
_TheDataBase.exec(sql, query_get_rows, &_page);
}
return true;
}
const TArray* TSQL_recordset::row(TRecnotype n)
{
const TArray* a = NULL;
if (move_to(n))
a = (const TArray*)_page.objptr(n-_firstrow);
return a;
}
const TVariant& TSQL_recordset::get(unsigned int c) const
{
const TArray* a = (const TArray*)_page.objptr(_current_row-_firstrow);
if (a != NULL)
{
const TVariant* s = (const TVariant*)a->objptr(c);
if (s != NULL)
return *s;
}
return NULL_VARIANT;
}
void TSQL_recordset::set(const char* sql)
{
reset();
_sql = sql;
if (_sql.find("SELECT") >= 0 || _sql.find("select") >= 0)
{
_TheDataBase.parse_select_from(_sql);
// Cerca le variabili nel testo SQL:
// Una variabile comincia per # ed e' composta da soli caratteri alfanumerici.
// Prima del simbolo # e dopo il nome della variabile deve esserci un separatore o blank
int diesis = _sql.find('#'); // cerco il primo #
for ( ; diesis > 0; diesis = _sql.find('#', diesis+1)) // Cerco tutti i #
{
if (is_var_separator(_sql[diesis-1])) // Controllo che ci sia un separatore prima del #
{
int i = diesis+1;
for ( ; _sql[i] && (isalnum(_sql[i]) || _sql[i] == '_'); i++);
if (i > diesis+1)
{
const TString& name = _sql.sub(diesis, i);
set_var(name, NULL_VARIANT, true);
}
}
}
}
}
TSQL_recordset::TSQL_recordset(const char* sql)
{
set(sql);
}
///////////////////////////////////////////////////////////
// TRecordset_sheet
///////////////////////////////////////////////////////////
void TRecordset_sheet::get_row(long r, TToken_string& row)
{
row.separator('\t');
row.cut(0);
if (_query.move_to(r))
{
TString str;
for (unsigned int c = 0; c < _query.columns(); c++)
{
_query.get(c).as_string(str);
row.add(str);
}
}
}
long TRecordset_sheet::get_items() const
{
return _query.items();
}
TRecordset_sheet::TRecordset_sheet(TRecordset& query)
: TSheet(-1, -1, -2, -4, "Query", query.sheet_head()), _query(query)
{
}