2004-05-07 10:25:51 +00:00
|
|
|
#ifdef WIN32
|
|
|
|
#include <fstream.h>
|
|
|
|
#else
|
|
|
|
#include "../xvaga/incstr.h"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include <diction.h>
|
|
|
|
#include <extcdecl.h>
|
|
|
|
#include <progind.h>
|
|
|
|
#include <recset.h>
|
|
|
|
#include <relation.h>
|
|
|
|
#include <utility.h>
|
|
|
|
#include <xml.h>
|
|
|
|
|
|
|
|
#include <statbar.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 (str != NULL)
|
|
|
|
{
|
|
|
|
if (_type == _alfafld)
|
|
|
|
*((TString*)_ptr) = str;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
set_null();
|
|
|
|
_type = _alfafld;
|
|
|
|
_ptr = new TString(str);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
set_null();
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool TVariant::is_zero() const
|
|
|
|
{
|
|
|
|
switch (_type)
|
|
|
|
{
|
|
|
|
case _datefld: return !as_date().ok();
|
|
|
|
case _longfld: return _ptr == NULL;
|
|
|
|
case _realfld: return as_real().is_zero();
|
|
|
|
case _alfafld: return real::is_null(as_string());
|
|
|
|
default: break;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool TVariant::is_empty() const
|
|
|
|
{
|
|
|
|
if (_type == _alfafld)
|
|
|
|
return as_string().empty();
|
|
|
|
return is_zero();
|
|
|
|
}
|
|
|
|
|
|
|
|
TDate TVariant::as_date() const
|
|
|
|
{
|
|
|
|
if (_type == _datefld)
|
|
|
|
return *(TDate*)_ptr;
|
2004-05-18 13:47:49 +00:00
|
|
|
if (_type == _intfld || _type == _realfld)
|
|
|
|
return TDate(as_int());
|
|
|
|
return TDate(as_string());
|
2004-05-07 10:25:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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)
|
2004-05-18 13:47:49 +00:00
|
|
|
ok = strchr("1XY", as_string()[0]) != NULL;
|
2004-05-07 10:25:51 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////
|
|
|
|
// TTable name converter
|
|
|
|
///////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
class TTable_names : public TObject
|
|
|
|
{
|
|
|
|
TAssoc_array _names;
|
|
|
|
TArray _ids;
|
|
|
|
long _filled;
|
|
|
|
|
|
|
|
protected:
|
|
|
|
void fill();
|
|
|
|
void add_file(int logic, const TString& table);
|
|
|
|
|
|
|
|
public:
|
|
|
|
const TString& name(int logic_num);
|
|
|
|
int logic_num(const TString& name);
|
|
|
|
|
|
|
|
TTable_names() : _filled(-1) { }
|
|
|
|
} _table_names;
|
|
|
|
|
|
|
|
void TTable_names::add_file(int logic, const TString& table)
|
|
|
|
{
|
|
|
|
TString8* id = new TString8;
|
|
|
|
id->format("%d", logic);
|
|
|
|
_names.add(table, id);
|
|
|
|
_ids.add(table, logic);
|
|
|
|
}
|
|
|
|
|
|
|
|
void TTable_names::fill()
|
|
|
|
{
|
|
|
|
if (_filled != prefix().get_codditta())
|
|
|
|
{
|
|
|
|
FileDes dir;
|
|
|
|
CGetFile(LF_DIR, &dir, _nolock, NORDIR);
|
|
|
|
const int nfiles = (int)dir.EOD;
|
|
|
|
|
|
|
|
TFilename n;
|
|
|
|
for (int logic = LF_USER; logic < nfiles; logic++)
|
|
|
|
{
|
|
|
|
const FileDes& fd = prefix().get_filedes(logic);
|
|
|
|
n = fd.SysName; n = n.name(); n.upper();
|
|
|
|
if (_names.objptr(n) == NULL)
|
|
|
|
add_file(logic, n);
|
|
|
|
}
|
|
|
|
_filled = prefix().get_codditta();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int TTable_names::logic_num(const TString& name)
|
|
|
|
{
|
|
|
|
if (isdigit(name[0]))
|
|
|
|
{
|
|
|
|
int num = atoi(name);
|
|
|
|
if (name[name.len()-1] == '@')
|
|
|
|
num = -num;
|
|
|
|
return num;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (name[0] == '%' && name.len() == 4)
|
|
|
|
return LF_TABCOM;
|
|
|
|
|
|
|
|
TString* str = (TString*)_names.objptr(name);
|
|
|
|
if (str == NULL)
|
|
|
|
{
|
|
|
|
fill();
|
|
|
|
str = (TString*)_names.objptr(name);
|
|
|
|
}
|
2004-05-18 13:47:49 +00:00
|
|
|
|
|
|
|
if (str == NULL && name.len() == 3)
|
|
|
|
return LF_TAB;
|
|
|
|
|
2004-05-07 10:25:51 +00:00
|
|
|
return str == NULL ? 0 : atoi(*str);
|
|
|
|
}
|
|
|
|
|
|
|
|
const TString& TTable_names::name(int logic_num)
|
|
|
|
{
|
|
|
|
TString* str = (TString*)_ids.objptr(logic_num);
|
|
|
|
if (str == NULL)
|
|
|
|
{
|
|
|
|
fill();
|
|
|
|
str = (TString*)_ids.objptr(logic_num);
|
|
|
|
}
|
|
|
|
return str == NULL ? (const TString&)EMPTY_STRING : *str;
|
|
|
|
}
|
|
|
|
|
|
|
|
const TString& logic2table(int logic_num)
|
|
|
|
{ return _table_names.name(logic_num); }
|
|
|
|
|
|
|
|
int table2logic(const TString& name)
|
|
|
|
{ return _table_names.logic_num(name); }
|
|
|
|
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////
|
|
|
|
// 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;
|
|
|
|
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 == _wordfld || ci._type == _intfld || ci._type == _longfld || ci._type == _realfld)
|
|
|
|
head << 'R';
|
|
|
|
}
|
|
|
|
|
|
|
|
// Creo la stringa temporanea solo ora, altrimenti puo' essere sovrascritta!
|
|
|
|
TToken_string& h = get_tmp_string();
|
|
|
|
h = head;
|
|
|
|
return h;
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
int TRecordset::find_column(const char* column_name) const
|
|
|
|
{
|
|
|
|
for (int i = columns()-1; i >= 0; i--)
|
|
|
|
{
|
|
|
|
const TRecordset_column_info& info = column_info(i);
|
|
|
|
if (info._name == column_name)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
|
|
|
|
const TVariant& TRecordset::get(const char* column_name) const
|
|
|
|
{
|
|
|
|
if (*column_name == '#')
|
2004-05-27 16:00:36 +00:00
|
|
|
return get_var(column_name);
|
2004-05-07 10:25:51 +00:00
|
|
|
|
|
|
|
char* colon = strchr(column_name, ':');
|
|
|
|
if (colon != NULL)
|
|
|
|
{
|
|
|
|
*colon = '\0';
|
|
|
|
const int i = find_column(column_name);
|
|
|
|
*colon = ':';
|
|
|
|
if (i >= 0)
|
|
|
|
{
|
|
|
|
const TString& str = get(i).as_string();
|
|
|
|
TString subfield; subfield << (colon+1) << '=';
|
|
|
|
int s = str.find(subfield);
|
|
|
|
if (s == 0 || (s > 0 && str[s-1] < ' '))
|
|
|
|
{
|
|
|
|
static TVariant var;
|
|
|
|
s += subfield.len();
|
|
|
|
const int e = str.find('\n', s);
|
|
|
|
var.set(str.sub(s, e));
|
|
|
|
return var;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
const int i = find_column(column_name);
|
|
|
|
if (i >= 0)
|
|
|
|
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 is_var_separator(char c)
|
|
|
|
{
|
|
|
|
if (isspace(c))
|
|
|
|
return true;
|
|
|
|
return strchr("<=>,", c) != NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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
|
|
|
|
void TRecordset::find_and_reset_vars()
|
|
|
|
{
|
|
|
|
_var.destroy();
|
|
|
|
_varnames.destroy();
|
|
|
|
|
|
|
|
const TString& sql = query_text();
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void TRecordset::parsed_text(TString& sql) const
|
|
|
|
{
|
|
|
|
sql = query_text();
|
|
|
|
const bool vars = ((TSQL_recordset*)this)->ask_variables(false);
|
|
|
|
if (vars) // Se ci sono variabili faccio le sostituzioni
|
|
|
|
{
|
|
|
|
const TString_array& names = variables();
|
2004-05-27 16:00:36 +00:00
|
|
|
TString s;
|
2004-05-07 10:25:51 +00:00
|
|
|
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);
|
2004-05-27 16:00:36 +00:00
|
|
|
|
|
|
|
if (var.type() == _datefld)
|
2004-05-28 11:16:48 +00:00
|
|
|
s.format("%ld", var.as_date().date2ansi());
|
2004-05-27 16:00:36 +00:00
|
|
|
else
|
2004-05-28 13:48:41 +00:00
|
|
|
{
|
2004-05-27 16:00:36 +00:00
|
|
|
s = var.as_string();
|
2004-05-28 13:48:41 +00:00
|
|
|
for (int i = 0; s[i]; i++)
|
|
|
|
{
|
|
|
|
if (s[i] == '\'')
|
|
|
|
s.insert("'", i++);
|
|
|
|
}
|
|
|
|
}
|
2004-05-07 10:25:51 +00:00
|
|
|
if ((var.is_string() && s[0] != '\'') || var.is_null())
|
|
|
|
{
|
|
|
|
s.insert("'");
|
|
|
|
s << '\'';
|
|
|
|
}
|
|
|
|
sql << s << after;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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 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)
|
|
|
|
{
|
|
|
|
ask_variable(*name, var);
|
|
|
|
if (var.is_null())
|
|
|
|
var.set(""); // Mi serve assolutamente un valore!
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
protected:
|
|
|
|
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 TEXT,time NUMERIC);\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 << "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;
|
|
|
|
}
|
|
|
|
|
|
|
|
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 :
|
|
|
|
{
|
|
|
|
const TDate date = curr.get_date(fd.Name);
|
|
|
|
tmp.set(date.date2ansi());
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case _boolfld : tmp.set(curr.get_bool(fd.Name)); break;
|
|
|
|
case _memofld:
|
|
|
|
{
|
|
|
|
TString memo = curr.get(fd.Name);
|
|
|
|
memo.replace('\n', char(0xB6)); // Simbolo di paragrafo
|
|
|
|
tmp.set(memo);
|
|
|
|
}
|
|
|
|
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';
|
|
|
|
get_sql_value(rec, rd.Fd[i], tmp);
|
|
|
|
sql << tmp.as_string();
|
|
|
|
}
|
|
|
|
sql << '\n';
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool TSQLite::import(int logicnum)
|
|
|
|
{
|
|
|
|
const TString& table = logic2table(logicnum);
|
|
|
|
|
|
|
|
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 << ' ';
|
|
|
|
switch (rd.Fd[i].TypeF)
|
|
|
|
{
|
|
|
|
case _alfafld: sql << "TEXT"; break;
|
|
|
|
case _memofld: sql << "BLOB"; break;
|
|
|
|
case _datefld: sql << "DATE"; break;
|
|
|
|
default : sql << "NUMERIC"; break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
sql << ");";
|
|
|
|
if (!exec(sql))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
// Creazione indici
|
|
|
|
for (int index = 0; index < rd.NKeys; index++)
|
|
|
|
{
|
|
|
|
sql.cut(0) << "CREATE INDEX " << table << '_' << (index+1) << " ON "<< table << "\n(";
|
|
|
|
const KeyDes& kd = rd.Ky[index];
|
|
|
|
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 << ");";
|
|
|
|
exec(sql);
|
|
|
|
}
|
|
|
|
|
|
|
|
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 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 = table2logic(table);
|
|
|
|
if (logicnum > 0)
|
|
|
|
import(logicnum);
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
TSQLite::TSQLite() : _handle(NULL)
|
|
|
|
{ }
|
|
|
|
|
|
|
|
TSQLite::~TSQLite()
|
|
|
|
{
|
|
|
|
close();
|
|
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////
|
|
|
|
// TSQL_recordset
|
|
|
|
///////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
void TSQL_recordset::reset()
|
|
|
|
{
|
|
|
|
_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 = _alfafld;
|
|
|
|
|
|
|
|
const char* fldtype = columns[argc+i];
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (_items < _pagesize)
|
|
|
|
{
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
_items++;
|
|
|
|
return SQLITE_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int query_get_items(void* jolly, int argc, char** values, char** columns)
|
|
|
|
{
|
|
|
|
TSQL_recordset* q = (TSQL_recordset*)jolly;
|
|
|
|
return q->on_get_items(argc, values, columns);
|
|
|
|
}
|
|
|
|
|
2004-05-28 13:48:41 +00:00
|
|
|
bool TSQL_recordset::set_var(const char* name, const TVariant& var, bool create)
|
2004-05-07 10:25:51 +00:00
|
|
|
{
|
2004-05-28 13:48:41 +00:00
|
|
|
const bool ok = TRecordset::set_var(name, var, create);
|
2004-05-07 10:25:51 +00:00
|
|
|
if (ok)
|
2004-05-28 13:48:41 +00:00
|
|
|
{
|
|
|
|
_items = 0;
|
2004-05-07 10:25:51 +00:00
|
|
|
_page.destroy();
|
2004-05-28 13:48:41 +00:00
|
|
|
}
|
2004-05-07 10:25:51 +00:00
|
|
|
return ok;
|
|
|
|
}
|
|
|
|
|
|
|
|
TRecnotype TSQL_recordset::items() const
|
|
|
|
{
|
|
|
|
if (_items == 0)
|
|
|
|
{
|
|
|
|
TString sql; parsed_text(sql);
|
|
|
|
TPerformance_profiler prof("SQL query");
|
|
|
|
_TheDataBase.exec("PRAGMA show_datatypes = ON;", NULL, NULL);
|
|
|
|
_TheDataBase.exec(sql, query_get_items, (TSQL_recordset*)this);
|
|
|
|
_TheDataBase.exec("PRAGMA show_datatypes = OFF;", NULL, NULL);
|
|
|
|
}
|
|
|
|
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];
|
|
|
|
}
|
|
|
|
|
|
|
|
// Funzione chiamata per riempire la pagina corrente delle righe della query
|
|
|
|
int TSQL_recordset::on_get_rows(int argc, char** values, char** columns)
|
|
|
|
{
|
|
|
|
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:
|
|
|
|
{
|
|
|
|
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 SQLITE_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int query_get_rows(void* jolly, int argc, char** values, char** columns)
|
|
|
|
{
|
|
|
|
TSQL_recordset* rs = (TSQL_recordset*)jolly;
|
|
|
|
return rs->on_get_rows(argc, values, columns);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool TSQL_recordset::move_to(TRecnotype n)
|
|
|
|
{
|
|
|
|
_current_row = n;
|
|
|
|
if (n < 0 || n >= items())
|
|
|
|
{
|
|
|
|
_page.destroy(); // Forza rilettura la prossiva volta
|
|
|
|
_first_row = 0;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (n < _first_row || n >= _first_row+_page.items())
|
|
|
|
{
|
|
|
|
TString sql; parsed_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)
|
|
|
|
_first_row = n-_pagesize/8; // Prendo qualche riga dalla pagina precedente, per velocizzare il pagina su
|
|
|
|
else
|
|
|
|
_first_row = n;
|
|
|
|
sql << "\nLIMIT " << _pagesize << " OFFSET " << _first_row << ';';
|
|
|
|
}
|
|
|
|
_TheDataBase.exec(sql, query_get_rows, this);
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
const TArray* TSQL_recordset::row(TRecnotype n)
|
|
|
|
{
|
|
|
|
const TArray* a = NULL;
|
|
|
|
if (move_to(n))
|
|
|
|
a = (const TArray*)_page.objptr(n-_first_row);
|
|
|
|
return a;
|
|
|
|
}
|
|
|
|
|
|
|
|
const TVariant& TSQL_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;
|
|
|
|
}
|
|
|
|
|
|
|
|
void TSQL_recordset::set(const char* sql)
|
|
|
|
{
|
|
|
|
reset();
|
|
|
|
_sql = sql;
|
|
|
|
if (_sql.find("SELECT") >= 0 || _sql.find("select") >= 0)
|
|
|
|
{
|
|
|
|
_TheDataBase.parse_select_from(_sql);
|
|
|
|
find_and_reset_vars();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
TSQL_recordset::TSQL_recordset(const char* sql)
|
|
|
|
{
|
|
|
|
set(sql);
|
|
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////
|
|
|
|
// TCursor_parser
|
|
|
|
///////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
class TCursor_parser
|
|
|
|
{
|
|
|
|
istream& _instr;
|
|
|
|
TArray& _column;
|
|
|
|
|
|
|
|
TString _pushed;
|
|
|
|
TString _token;
|
|
|
|
|
|
|
|
TRelation* _relation;
|
|
|
|
TCursor* _cursor;
|
|
|
|
|
|
|
|
protected:
|
|
|
|
const TString& pop();
|
|
|
|
void push();
|
|
|
|
void add_column_info(const char* table, const TRectype& rec);
|
|
|
|
|
|
|
|
void parse_sortexpr(TToken_string& se);
|
|
|
|
void parse_filter(TToken_string& filter);
|
|
|
|
void parse_region(TRectype& rec);
|
|
|
|
void parse_join_param(TRelation* rel, const TString& j, int to);
|
|
|
|
void parse_join();
|
|
|
|
void parse_sortedjoin();
|
|
|
|
|
|
|
|
public:
|
|
|
|
TRelation* get_relation() { return _relation; }
|
|
|
|
TCursor* get_cursor() { return _cursor; }
|
|
|
|
|
|
|
|
TCursor_parser(istream& instr, TArray& column);
|
|
|
|
};
|
|
|
|
|
|
|
|
const TString& TCursor_parser::pop()
|
|
|
|
{
|
|
|
|
if (_pushed.not_empty())
|
|
|
|
{
|
|
|
|
_token = _pushed;
|
|
|
|
_pushed.cut(0);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
_token.cut(0);
|
|
|
|
|
|
|
|
_instr.eatwhite();
|
|
|
|
if (_instr.eof())
|
|
|
|
return _token;
|
|
|
|
|
|
|
|
char c;
|
|
|
|
_instr.get(c);
|
|
|
|
while (!isspace(c) && c != EOF)
|
|
|
|
{
|
|
|
|
_token << c;
|
|
|
|
_instr.get(c);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return _token;
|
|
|
|
}
|
|
|
|
|
|
|
|
void TCursor_parser::push()
|
|
|
|
{
|
|
|
|
CHECK(_pushed.empty(), "Repushing?");
|
|
|
|
_pushed = _token;
|
|
|
|
}
|
|
|
|
|
|
|
|
void TCursor_parser::add_column_info(const char* table, const TRectype& rec)
|
|
|
|
{
|
|
|
|
for (int i = 0; i < rec.items(); i++)
|
|
|
|
{
|
|
|
|
TRecordset_column_info* info = new TRecordset_column_info;
|
|
|
|
const char* name = rec.fieldname(i);
|
|
|
|
info->_name << table << '.' << name;
|
|
|
|
info->_type = rec.type(name);
|
|
|
|
switch (info->_type)
|
|
|
|
{
|
|
|
|
case _datefld: info->_width = 10; break;
|
|
|
|
case _memofld: info->_width = 50; break;
|
|
|
|
default : info->_width = rec.length(name); break;
|
|
|
|
}
|
|
|
|
_column.add(info);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void TCursor_parser::parse_sortexpr(TToken_string& se)
|
|
|
|
{
|
|
|
|
const char sep = se.separator();
|
|
|
|
se.separator(' ');
|
|
|
|
_instr.getline(se.get_buffer(), se.size());
|
|
|
|
se.strip_d_spaces();
|
|
|
|
se.replace(' ', sep);
|
|
|
|
se.separator(sep);
|
|
|
|
}
|
|
|
|
|
|
|
|
void TCursor_parser::parse_filter(TToken_string& filter)
|
|
|
|
{
|
|
|
|
const TString& str = pop();
|
|
|
|
while (str.find('=') > 0)
|
|
|
|
{
|
|
|
|
filter.add(str);
|
|
|
|
pop();
|
|
|
|
}
|
|
|
|
push();
|
|
|
|
}
|
|
|
|
|
|
|
|
void TCursor_parser::parse_region(TRectype& rec)
|
|
|
|
{
|
|
|
|
TString16 field;
|
|
|
|
TString value;
|
|
|
|
while (true)
|
|
|
|
{
|
|
|
|
const TString& ass = pop();
|
|
|
|
const int equal = ass.find('=');
|
|
|
|
if (equal > 0)
|
|
|
|
{
|
|
|
|
field = ass.left(equal);
|
|
|
|
value = ass.mid(equal+1);
|
|
|
|
if (value[0] == '"' || value[0] == '\'')
|
|
|
|
{
|
|
|
|
value.rtrim(1);
|
|
|
|
value.ltrim(1);
|
|
|
|
}
|
|
|
|
rec.put(field, value);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
push();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void TCursor_parser::parse_join_param(TRelation* rel, const TString& j, int to)
|
|
|
|
{
|
|
|
|
int key = 1;
|
|
|
|
const TString& tok = pop();
|
|
|
|
if (tok.starts_with("KE"))
|
|
|
|
{
|
|
|
|
pop();
|
|
|
|
key = atoi(tok);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
push();
|
|
|
|
|
|
|
|
int alias = 0;
|
|
|
|
pop();
|
|
|
|
if (tok.starts_with("AL"))
|
|
|
|
{
|
|
|
|
pop();
|
|
|
|
alias = atoi(tok);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
push();
|
|
|
|
|
|
|
|
TToken_string exp(80);
|
|
|
|
pop();
|
|
|
|
if (tok == "INTO")
|
|
|
|
{
|
|
|
|
parse_filter(exp);
|
|
|
|
}
|
|
|
|
if (exp.empty())
|
|
|
|
yesnofatal_box("JOIN senza espressioni INTO");
|
|
|
|
|
|
|
|
const int logicnum = table2logic(j);
|
|
|
|
if (logicnum != LF_TAB && logicnum != LF_TABCOM)
|
|
|
|
rel->add(logicnum, exp, key, to, alias); // join file
|
|
|
|
else
|
|
|
|
rel->add(j, exp, key, to, alias); // join table
|
|
|
|
|
|
|
|
TString16 tabname;
|
|
|
|
if (alias > 0)
|
|
|
|
tabname << alias << '@';
|
|
|
|
else
|
|
|
|
tabname = j;
|
|
|
|
const TRectype& rec = rel->curr(logicnum);
|
|
|
|
add_column_info(tabname, rec);
|
|
|
|
}
|
|
|
|
|
|
|
|
void TCursor_parser::parse_join()
|
|
|
|
{
|
|
|
|
const TString j = pop(); // File or table
|
|
|
|
|
|
|
|
int to = 0;
|
|
|
|
const TString& tok = pop();
|
|
|
|
if (tok == "TO") // TO keyword
|
|
|
|
{
|
|
|
|
pop();
|
|
|
|
to = table2logic(tok);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
push();
|
|
|
|
|
|
|
|
parse_join_param(_relation, j, to);
|
|
|
|
}
|
|
|
|
|
|
|
|
void TCursor_parser::parse_sortedjoin()
|
|
|
|
{
|
|
|
|
TToken_string filter,sortexp;
|
|
|
|
const TString j = pop(); // File or table
|
|
|
|
const TString& tok = pop();
|
|
|
|
if (tok == "BY" )
|
|
|
|
{
|
|
|
|
parse_sortexpr(sortexp);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
push();
|
|
|
|
|
|
|
|
pop();
|
|
|
|
if (tok.starts_with("FI") || tok.starts_with("SE"))
|
|
|
|
{
|
|
|
|
parse_filter(filter);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
push();
|
|
|
|
|
|
|
|
TRelation* sortrel = new TRelation(table2logic(j));
|
|
|
|
while (true)
|
|
|
|
{
|
|
|
|
pop();
|
|
|
|
if (tok.empty() || tok.starts_with("JO"))
|
|
|
|
{
|
|
|
|
push();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (tok.starts_with("US")) // USING keyword
|
|
|
|
{
|
|
|
|
const TString subj = pop(); // File or table
|
|
|
|
parse_join_param(sortrel, subj, table2logic(j));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int to = 0;
|
|
|
|
pop();
|
|
|
|
if (tok == "TO") // TO keyword
|
|
|
|
{
|
|
|
|
pop();
|
|
|
|
to = table2logic(tok);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
push();
|
|
|
|
|
|
|
|
int key = 1;
|
|
|
|
pop();
|
|
|
|
if (tok.starts_with("KE"))
|
|
|
|
{
|
|
|
|
pop();
|
|
|
|
key = atoi(tok);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
push();
|
|
|
|
|
|
|
|
int alias = 0;
|
|
|
|
pop();
|
|
|
|
if (tok.starts_with("AL"))
|
|
|
|
{
|
|
|
|
pop();
|
|
|
|
alias = atoi(tok);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
push();
|
|
|
|
|
|
|
|
TToken_string exp(80);
|
|
|
|
if (pop() == "INTO")
|
|
|
|
{
|
|
|
|
pop();
|
|
|
|
while (tok.find('=') > 0)
|
|
|
|
{
|
|
|
|
exp.add(tok);
|
|
|
|
pop();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
push();
|
|
|
|
|
|
|
|
TSortedfile *sf= new TSortedfile(atoi(j),sortrel,sortexp,filter,key);
|
|
|
|
_relation->add((TLocalisamfile *)sf, exp, key, to, alias, false); // join table
|
|
|
|
|
|
|
|
TString16 tabname = j;
|
|
|
|
if (alias > 0)
|
|
|
|
tabname.cut(0) << alias << '@';
|
|
|
|
add_column_info(tabname, sf->curr());
|
|
|
|
}
|
|
|
|
|
|
|
|
TCursor_parser::TCursor_parser(istream& instr, TArray& col)
|
|
|
|
: _instr(instr), _column(col), _relation(NULL), _cursor(NULL)
|
|
|
|
{
|
|
|
|
_column.destroy();
|
|
|
|
const TString& tok = pop();
|
|
|
|
if (!tok.starts_with("US"))
|
|
|
|
push();
|
|
|
|
|
|
|
|
pop();
|
|
|
|
if (tok.blank())
|
|
|
|
return;
|
|
|
|
|
|
|
|
const int logicnum = table2logic(tok);
|
|
|
|
const char* tab = NULL;
|
|
|
|
|
|
|
|
if (logicnum != LF_TAB && logicnum != LF_TABCOM)
|
|
|
|
_relation = new TRelation(logicnum);
|
|
|
|
else
|
|
|
|
_relation = new TRelation(tok);
|
|
|
|
add_column_info(tok, _relation->curr());
|
|
|
|
|
|
|
|
int key = 1; // key number
|
|
|
|
pop();
|
|
|
|
if (tok.starts_with("KE"))
|
|
|
|
{
|
|
|
|
pop();
|
|
|
|
key = atoi(tok);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
push();
|
|
|
|
|
|
|
|
pop();
|
|
|
|
if (tok.starts_with("BY")) // "sort BY": user-defined sort
|
|
|
|
{
|
|
|
|
TToken_string ordexpr(256);
|
|
|
|
parse_sortexpr(ordexpr);
|
|
|
|
_cursor = new TSorted_cursor(_relation, ordexpr,"", key);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
push();
|
|
|
|
|
|
|
|
pop();
|
|
|
|
if (tok.starts_with("FI") || tok.starts_with("SE"))
|
|
|
|
{
|
|
|
|
TToken_string filter;
|
|
|
|
parse_filter(filter);
|
|
|
|
if (_cursor == NULL)
|
|
|
|
_cursor = new TCursor(_relation, filter, key);
|
|
|
|
else
|
|
|
|
_cursor->setfilter(filter);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
push();
|
|
|
|
|
|
|
|
if (_cursor == NULL)
|
|
|
|
_cursor = new TCursor(_relation, "", key);
|
|
|
|
|
|
|
|
TRectype rec_start(_relation->curr());
|
|
|
|
TRectype rec_stop(_relation->curr());
|
|
|
|
|
|
|
|
pop();
|
|
|
|
if (tok.starts_with("FR"))
|
|
|
|
parse_region(rec_start);
|
|
|
|
else
|
|
|
|
push();
|
|
|
|
pop();
|
|
|
|
if (tok.starts_with("TO"))
|
|
|
|
parse_region(rec_stop);
|
|
|
|
else
|
|
|
|
push();
|
|
|
|
if (!rec_start.empty() || !rec_stop.empty())
|
|
|
|
_cursor->setregion(rec_start, rec_stop, 0x2);
|
|
|
|
|
|
|
|
while (true)
|
|
|
|
{
|
|
|
|
pop();
|
|
|
|
if (tok.starts_with("JO"))
|
|
|
|
parse_join(); else
|
|
|
|
if (tok.starts_with("SO"))
|
|
|
|
parse_sortedjoin();
|
|
|
|
else
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
push();
|
|
|
|
|
|
|
|
if (_relation->items() == 0) // Non ci sono anche tabelle collegate
|
|
|
|
{
|
|
|
|
FOR_EACH_ARRAY_ITEM(_column, i, obj)
|
|
|
|
{
|
|
|
|
TRecordset_column_info* info = (TRecordset_column_info*)obj;
|
|
|
|
const int arrow = info->_name.find('.');
|
|
|
|
if (arrow > 0)
|
|
|
|
info->_name = info->_name.mid(arrow+1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////
|
|
|
|
// TISAM_recordset
|
|
|
|
///////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
TVariant& TISAM_recordset::get_tmp_var() const
|
|
|
|
{
|
|
|
|
static TArray _page; // Variants to be returned by get
|
|
|
|
static int _next_var = 0; // Index of next variant to be returned
|
|
|
|
|
|
|
|
if (_next_var >= 32)
|
|
|
|
_next_var = 0;
|
|
|
|
TVariant* var = (TVariant*)_page.objptr(_next_var);
|
|
|
|
if (var == NULL)
|
|
|
|
{
|
|
|
|
var = new TVariant;
|
|
|
|
_page.add(var, _next_var);
|
|
|
|
}
|
|
|
|
_next_var++;
|
|
|
|
return *var;
|
|
|
|
}
|
|
|
|
|
|
|
|
const TVariant& TISAM_recordset::get(int logic, const char* fldname) const
|
|
|
|
{
|
|
|
|
TString80 name = fldname;
|
|
|
|
TString16 subfield;
|
|
|
|
int from = 1, to = 0;
|
|
|
|
|
|
|
|
const int open_bracket = name.find('[');
|
|
|
|
if (open_bracket > 0)
|
|
|
|
{
|
|
|
|
sscanf((const char*)name + open_bracket, "[%d,%d]", &from, &to);
|
|
|
|
name.cut(open_bracket);
|
|
|
|
}
|
|
|
|
|
|
|
|
const int colon = name.find(':');
|
|
|
|
if (colon > 0)
|
|
|
|
{
|
|
|
|
subfield = name.mid(colon+1);
|
|
|
|
name.cut(colon);
|
|
|
|
}
|
|
|
|
|
2004-05-18 13:47:49 +00:00
|
|
|
const TRectype& rec = relation()->curr(logic);
|
2004-05-07 10:25:51 +00:00
|
|
|
const TFieldtypes ft = rec.type(name);
|
|
|
|
|
|
|
|
if (ft == _nullfld)
|
|
|
|
{
|
|
|
|
if (logic == LF_DOC) // Proviamo la magia
|
|
|
|
{
|
|
|
|
subfield = name;
|
|
|
|
name = "G1";
|
|
|
|
}
|
|
|
|
else
|
|
|
|
return NULL_VARIANT;
|
|
|
|
}
|
|
|
|
|
|
|
|
TVariant& var = get_tmp_var();
|
|
|
|
switch (ft)
|
|
|
|
{
|
|
|
|
case _datefld: var.set(rec.get_date(name)); break;
|
|
|
|
case _realfld: var.set(rec.get_real(name)); break;
|
|
|
|
case _intfld :
|
|
|
|
case _longfld:
|
|
|
|
case _wordfld: var.set(rec.get_long(name)); break;
|
|
|
|
default : var.set(rec.get(name)); break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (subfield.not_empty())
|
|
|
|
{
|
|
|
|
subfield << '=';
|
|
|
|
const TString& str = var.as_string();
|
|
|
|
int s = str.find(subfield);
|
|
|
|
if (s == 0 || (s > 0 && str[s-1] < ' '))
|
|
|
|
{
|
|
|
|
s += subfield.len();
|
|
|
|
const int e = str.find('\n', s);
|
|
|
|
var.set(str.sub(s, e));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (to >= from)
|
|
|
|
var.set(var.as_string().sub(from-1, to));
|
|
|
|
|
|
|
|
return var;
|
|
|
|
}
|
|
|
|
|
|
|
|
const TVariant& TISAM_recordset::get(size_t c) const
|
|
|
|
{
|
|
|
|
const TRecordset_column_info* info = (const TRecordset_column_info*)_column.objptr(c);
|
|
|
|
if (info != NULL)
|
|
|
|
{
|
|
|
|
int logic = 0;
|
|
|
|
const char* field = info->_name;
|
|
|
|
const int dot = info->_name.find('.');
|
|
|
|
if (dot > 0)
|
|
|
|
{
|
|
|
|
logic = table2logic(info->_name.left(dot));
|
|
|
|
field += dot+1;
|
|
|
|
}
|
|
|
|
return get(logic, field);
|
|
|
|
}
|
|
|
|
return NULL_VARIANT;
|
|
|
|
}
|
|
|
|
|
|
|
|
const TVariant& TISAM_recordset::get(const char* name) const
|
|
|
|
{
|
2004-05-27 16:00:36 +00:00
|
|
|
if (*name == '#')
|
|
|
|
return get_var(name);
|
|
|
|
|
2004-05-07 10:25:51 +00:00
|
|
|
const TFixed_string fldname(name);
|
|
|
|
|
|
|
|
int table_end = fldname.find('.');
|
|
|
|
int field_start = table_end+1;
|
|
|
|
if (table_end < 0)
|
|
|
|
{
|
|
|
|
table_end = fldname.find('-');
|
|
|
|
if (table_end > 0)
|
|
|
|
field_start = table_end+2;
|
|
|
|
}
|
|
|
|
int logic = 0;
|
|
|
|
const char* field = name;
|
|
|
|
if (table_end > 0)
|
|
|
|
{
|
|
|
|
logic = table2logic(fldname.left(table_end));
|
|
|
|
field += field_start;
|
|
|
|
}
|
|
|
|
return get(logic, field);
|
|
|
|
}
|
|
|
|
|
|
|
|
const TRecordset_column_info& TISAM_recordset::column_info(size_t i) const
|
|
|
|
{
|
|
|
|
return (const TRecordset_column_info&)_column[i];
|
|
|
|
}
|
|
|
|
|
2004-05-18 13:47:49 +00:00
|
|
|
TCursor* TISAM_recordset::cursor() const
|
2004-05-07 10:25:51 +00:00
|
|
|
{
|
2004-05-18 13:47:49 +00:00
|
|
|
if (_cursor == NULL && !_use.blank())
|
2004-05-07 10:25:51 +00:00
|
|
|
{
|
|
|
|
TString use; parsed_text(use);
|
|
|
|
TPerformance_profiler prof("ISAM query");
|
|
|
|
TISAM_recordset* my = (TISAM_recordset*)this;
|
|
|
|
istrstream instr(use.get_buffer(), use.len());
|
|
|
|
TCursor_parser parser(instr, my->_column);
|
|
|
|
|
|
|
|
my->_relation = parser.get_relation();
|
|
|
|
my->_cursor = parser.get_cursor();
|
|
|
|
|
|
|
|
if (_cursor != NULL)
|
|
|
|
{
|
|
|
|
_cursor->items();
|
|
|
|
_cursor->freeze();
|
|
|
|
}
|
|
|
|
}
|
2004-05-18 13:47:49 +00:00
|
|
|
return _cursor;
|
|
|
|
}
|
|
|
|
|
|
|
|
TRelation* TISAM_recordset::relation() const
|
|
|
|
{
|
|
|
|
cursor();
|
|
|
|
return _relation;
|
|
|
|
}
|
|
|
|
|
|
|
|
TRecnotype TISAM_recordset::current_row() const
|
|
|
|
{
|
|
|
|
TCursor* c = cursor();
|
|
|
|
return c != NULL ? c->pos() : -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
TRecnotype TISAM_recordset::items() const
|
|
|
|
{
|
|
|
|
TCursor* c = cursor();
|
|
|
|
return c != NULL ? c->items() : -1;
|
2004-05-07 10:25:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
unsigned int TISAM_recordset::columns() const
|
|
|
|
{
|
2004-05-18 13:47:49 +00:00
|
|
|
cursor();
|
2004-05-07 10:25:51 +00:00
|
|
|
return _column.items();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool TISAM_recordset::move_to(TRecnotype pos)
|
|
|
|
{
|
2004-05-18 13:47:49 +00:00
|
|
|
TCursor* c = cursor();
|
|
|
|
bool ok = c != NULL;
|
2004-05-07 10:25:51 +00:00
|
|
|
if (ok)
|
|
|
|
{
|
2004-05-18 13:47:49 +00:00
|
|
|
*c = pos;
|
2004-05-07 10:25:51 +00:00
|
|
|
ok = pos >= 0 && pos < items();
|
|
|
|
}
|
|
|
|
return ok;
|
|
|
|
}
|
|
|
|
|
|
|
|
void TISAM_recordset::reset()
|
|
|
|
{
|
|
|
|
_column.destroy();
|
|
|
|
if (_relation != NULL)
|
|
|
|
{
|
|
|
|
delete _relation;
|
|
|
|
_relation = NULL;
|
|
|
|
}
|
|
|
|
if (_cursor != NULL)
|
|
|
|
{
|
|
|
|
delete _cursor;
|
|
|
|
_cursor = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2004-05-28 13:48:41 +00:00
|
|
|
bool TISAM_recordset::set_var(const char* name, const TVariant& var, bool create)
|
|
|
|
{
|
|
|
|
const bool ok = TRecordset::set_var(name, var, create);
|
|
|
|
if (ok)
|
|
|
|
reset();
|
|
|
|
return ok;
|
|
|
|
}
|
|
|
|
|
2004-05-07 10:25:51 +00:00
|
|
|
void TISAM_recordset::set(const char* use)
|
|
|
|
{
|
|
|
|
reset();
|
|
|
|
_use = use;
|
|
|
|
find_and_reset_vars();
|
|
|
|
}
|
|
|
|
|
|
|
|
TISAM_recordset::TISAM_recordset(const char* use)
|
|
|
|
: _relation(NULL), _cursor(NULL)
|
|
|
|
{
|
|
|
|
set(use);
|
|
|
|
}
|
|
|
|
|
|
|
|
TISAM_recordset::~TISAM_recordset()
|
|
|
|
{
|
|
|
|
reset();
|
|
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////
|
|
|
|
// 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)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////
|
|
|
|
// TPerformance_profiler
|
|
|
|
///////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
TPerformance_profiler::TPerformance_profiler(const char* desc)
|
|
|
|
: _desc(desc)
|
|
|
|
{
|
|
|
|
#ifdef DBG
|
|
|
|
_start = clock();
|
|
|
|
|
|
|
|
TString80 msg;
|
|
|
|
msg << "Profiling " << desc << "...";
|
|
|
|
statbar_set_title(TASK_WIN, msg);
|
|
|
|
|
|
|
|
while (true)
|
|
|
|
{
|
|
|
|
const clock_t clk = clock();
|
|
|
|
if (clk != _start)
|
|
|
|
{
|
|
|
|
_start = clk;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
void TPerformance_profiler::show() const
|
|
|
|
{
|
|
|
|
#ifdef DBG
|
|
|
|
const double s = double(clock() - _start) / CLOCKS_PER_SEC;
|
|
|
|
|
|
|
|
int hour = 0, min = 0;
|
|
|
|
int sec = int(s);
|
|
|
|
const int cent = int((s - sec)*100);
|
|
|
|
|
|
|
|
if (sec >= 3600)
|
|
|
|
{
|
|
|
|
hour = sec / 3600;
|
|
|
|
sec -= hour * 3600;
|
|
|
|
}
|
|
|
|
if (sec >= 60)
|
|
|
|
{
|
|
|
|
min = sec / 60;
|
|
|
|
sec -= min * 60;
|
|
|
|
}
|
|
|
|
|
|
|
|
TString80 msg = _desc;
|
|
|
|
msg.format("%s %02d:%02d:%02d.%02d", (const char*)_desc, hour, min, sec, cent);
|
|
|
|
statbar_set_title(TASK_WIN, msg);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
TPerformance_profiler::~TPerformance_profiler()
|
|
|
|
{
|
|
|
|
show();
|
|
|
|
}
|