campo-sirio/include/recset.cpp
alex 21699c002e Patch level : 2.2 399
Files correlati     :
Ricompilazione Demo : [ ]
Commento            :

Riportata la versione 2.1 598


git-svn-id: svn://10.65.10.50/trunk@13909 c028cbd2-c16b-5b4b-a496-9718f37d4682
2006-04-13 17:56:02 +00:00

2292 lines
49 KiB
C++
Executable File

#include "../xvaga/incstr.h"
#include <applicat.h>
#include <extcdecl.h>
#include <modaut.h>
#include <progind.h>
#include <recset.h>
#include <relation.h>
#include <utility.h>
#include <xml.h>
#include <statbar.h>
///////////////////////////////////////////////////////////
// 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)
{
TString* 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& n)
{
// Non cambiare: n puo' essere temporaneo e pieno di spazi!
TString80 name = n; name.trim();
if (isdigit(name[0]))
{
int num = atoi(name);
if (name.ends_with("@"))
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);
}
if (str == NULL && name.len() == 3)
return LF_TAB;
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;
TString qry; parsed_text(qry);
for (int i = qry.find('\n'); i > 0; i = qry.find('\n', i+1))
qry.insert("<br/>", i+1);
out << "<p><b>" << qry << "</b></p>" << endl;
out << "<table border=\"1\">" << 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;
// Larghezza colonne
for (unsigned int h = 0; h < columns(); h++)
{
const TRecordset_column_info& ci = column_info(h);
const int w = max(ci._width, ci._name.len());
out << "F;W" << (h+1) << ' ' << (h+1) << ' ' << w << endl;
}
// Intestazioni colonne
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++)
{
const TRecordset_column_info& ci = column_info(c);
const bool is_alpha = ci._type == _alfafld || ci._type == _charfld ||
ci._type == _memofld || ci._type == _datefld;
out << "C;Y" << (n+2) << ";X" << (c+1) << ";K";
if (is_alpha) out << '"';
get(c).as_string(val);
if (!val.blank())
{
val.rtrim();
val.replace('"', '\'');
out << val;
}
if (is_alpha) out << '"';
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, int mode)
{
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;
case fmt_dbf : ok = save_as_dbf(path, mode); break;
default : ok = save_as_text(path); break;
}
return ok;
}
int TRecordset::find_column(const char* column_name) const
{
int i;
for (i = columns()-1; i >= 0; i--)
{
const TRecordset_column_info& info = column_info(i);
if (info._name == column_name)
break;
}
return i;
}
TVariant& TRecordset::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& TRecordset::get(const char* column_name) const
{
if (*column_name == '#')
return get_var(column_name);
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
{
// Se mi accorgo che posso e voglio accedere ad un campo del recordset padre
if (_parentset != NULL && strncmp(name, "#PARENT.", 8) == 0)
{
return _parentset->get(name+8); // Attenzione! E' giusto usare get() e non get_var()
}
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;
}
}
if (ok)
requery();
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] == '_' || 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 is_isam = sql.starts_with("US");
const bool is_sql = !is_isam;
const char* apici = is_isam ? "\"" : "'";
const bool vars = ((TSQL_recordset*)this)->ask_variables(false);
if (vars) // Se ci sono variabili faccio le sostituzioni
{
const TString_array& names = variables();
TString s;
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);
if (var.type() == _datefld)
s.format("%ld", var.as_date().date2ansi());
else
{
s = var.as_string();
if (is_sql) // Raddoppia gli apici in SQL
{
for (int i = 0; s[i]; i++)
{
if (s[i] == '\'')
s.insert("'", i++);
}
}
}
if ((var.is_string() && s[0] != *apici && sql.right(1) != apici) || var.is_null())
{
s.insert(apici);
s << apici;
}
sql << s << after;
}
}
}
}
bool ask_variable(const char* name, TVariant& var)
{
TMask m(TR("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;
}
TRecordset::TRecordset() : _parentset(NULL)
{ }
///////////////////////////////////////////////////////////
// Utility
///////////////////////////////////////////////////////////
static void sort_files(TString_array& files)
{
TFilename path;
// Trasforma i path completi in nomi senza estensione
FOR_EACH_ARRAY_ROW(files, i, row)
{
path = *row;
path = path.name();
path.ext("");
path.lower();
*row = path;
}
files.sort(); // Ordina alfabeticamente
// Rimuove i files doppi proveninenti da Campo e Custom
for (int j = files.last(); j > 0; j--)
{
if (files.row(j) == files.row(j-1))
files.destroy(j);
}
}
static bool get_xml_attr(const TString& line, const char* attr, TString& value)
{
TString80 str; str << ' ' << attr << "=\"";
const int pos = line.find(str);
if (pos >= 0)
{
const int apicia = line.find('"', pos)+1;
const int apicic = line.find('"', apicia);
if (apicic > apicia)
{
value = line.sub(apicia, apicic);
return true;
}
}
value.cut(0);
return false;
}
static bool get_xml_child(const TString& line, const char* tag, TString& value)
{
TString80 str; str << '<' << tag << '>';
const int pos = line.find(str);
if (pos >= 0)
{
const int apicia = line.find('>', pos)+1;
const int apicic = line.find('<', apicia);
if (apicic > apicia)
{
value = line.sub(apicia, apicic);
return true;
}
}
value.cut(0);
return false;
}
bool list_custom_files(const char* ext, const char* classe, TString_array& files)
{
TString_array lista;
TFilename path;
{
TWait_cursor hourglass;
TString_array files;
// Leggo i files in custom
if (main_app().has_module(RSAUT))
{
TFilename custom = firm2dir(-1);
custom.add("custom");
if (!custom.exist())
xvt_fsys_mkdir(custom);
path = custom;
path.add("*");
path.ext(ext);
list_files(path, lista);
}
path = "*"; path.ext(ext); // Leggo i files in campo
list_files(path, lista);
sort_files(lista); // Ordino i files e rimuovo i doppioni
TString8 acqua;
TString stringona, desc;
FOR_EACH_ARRAY_ROW(lista, i, row)
{
path = *row; path.ext(ext);
bool ok = path.custom_path();
if (ok)
{
TScanner scan(path);
stringona.cut(0);
for (int i = 0; i < 3 && scan.good(); i++) // Leggo solo le prime righe
stringona << scan.line();
get_xml_attr(stringona, "class", acqua);
if (classe && *classe)
ok = acqua == classe;
if (ok)
{
get_xml_child(stringona, "description", desc);
TToken_string* riga = new TToken_string;
riga->add(*row);
riga->add(acqua);
riga->add(desc);
files.add(riga);
}
}
}
return !files.empty();
}
bool select_custom_file(TFilename& path, const char* ext, const char* library)
{
TArray_sheet sheet(-1, -1, 80, 20, TR("Selezione"), HR("Nome@8|Classe|Descrizione@50|Custom"));
TString_array& files = sheet.rows_array();
bool ok = list_custom_files(ext, library, files);
if (ok)
{
TFilename name;
FOR_EACH_ARRAY_ROW(files, i, row)
{
name = row->get(0);
name.ext(ext);
name.custom_path();
if (name.find("custom") > 0)
row->add("X");
}
ok = sheet.run() == K_ENTER;
if (ok)
{
path = sheet.row(-1).get(0);
path.ext(ext);
ok = path.custom_path();
}
}
return ok;
}
///////////////////////////////////////////////////////////
// Private interface
///////////////////////////////////////////////////////////
#ifdef SQLITE3
#include "../sqlite3/sqlite3.h"
#else
#include "../sqlite/sqlite.h"
#endif
class TSQLite : public TObject
{
#ifdef SQLITE3
sqlite3* _handle;
#else
sqlite* _handle;
#endif
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();
#ifdef SQLITE3
bool bind_record(const TRectype& rec, const RecDes& rd, sqlite3_stmt* pStatement) const;
#else
bool esporta(const TRectype& rec, ostream& sql) const;
#endif
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:
#ifdef SQLITE3
sqlite3* open(const char* fname = NULL);
bool exec(const char* sql, sqlite3_callback callback = NULL, void* jolly = NULL, bool show_error = true);
#else
sqlite* open(const char* fname = NULL);
bool exec(const char* sql, sqlite_callback callback = NULL, void* jolly = NULL, bool show_error = true);
#endif
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);
}
#ifdef SQLITE3
sqlite3* TSQLite::open(const char* fname)
{
close();
_currdb = fname;
int err = sqlite3_open(_currdb, &_handle);
if (err = SQLITE_CORRUPT)
{
close();
xvt_fsys_removefile(_currdb);
err = sqlite3_open(_currdb, &_handle);
}
if (err == SQLITE_OK)
{
create_dbf_times();
}
else
{
const char* errmsg = sqlite3_errmsg(_handle); // Stringa di sitema: inutile sqlite3_free(errmsg)
error_box(errmsg);
}
return _handle;
}
#else
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;
}
#endif
void TSQLite::test_path()
{
TFilename n;
build_curr_path(n);
if (n != _currdb)
open(n);
}
#ifdef SQLITE3
bool TSQLite::exec(const char* sql, sqlite3_callback callback, void* jolly, bool show_error)
#else
bool TSQLite::exec(const char* sql, sqlite_callback callback, void* jolly, bool show_error)
#endif
{
if (_handle == NULL)
test_path();
TWait_cursor hourglass;
char* errmsg = NULL;
#ifdef SQLITE3
const int rc = sqlite3_exec(_handle, sql, callback, jolly, &errmsg);
#else
const int rc = sqlite_exec(_handle, sql, callback, jolly, &errmsg);
#endif
if (errmsg != NULL)
{
if (show_error)
{
TString msg;
msg << sql;
msg.cut(128);
msg << '\n' << errmsg;
error_box(msg);
}
#ifdef SQLITE3
sqlite3_free(errmsg);
#else
sqlite_freemem(errmsg);
#endif
}
return rc == SQLITE_OK;
}
void TSQLite::close()
{
if (_handle != NULL)
{
#ifdef SQLITE3
sqlite3_close(_handle);
#else
sqlite_close(_handle);
#endif
_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;
}
#ifdef SQLITE3
bool TSQLite::bind_record(const TRectype& rec, const RecDes& rd, sqlite3_stmt* pStatement) const
{
int rc = SQLITE_OK;
TVariant tmp;
for (int i = 0; i < rd.NFields && rc==SQLITE_OK ; i++)
{
get_sql_value(rec, rd.Fd[i], tmp);
const TString& val = tmp.as_string();
rc = sqlite3_bind_text(pStatement, i+1, val, val.len(), SQLITE_TRANSIENT);
}
return rc == SQLITE_OK;
}
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(";
int i;
for (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;
TRelation rel(logicnum);
TCursor cur(&rel);
const TRecnotype items = cur.items();
if (items > 0)
{
cur.freeze();
TString msg;
msg << TR("Importazione tabella") << ' ' << table;
msg << ": " << items << ' ' << TR("righe");
TProgind pi(items, msg, true, true);
exec("BEGIN"); // Inizio transazione
// Creo il comando INSERT INTO table VALUES(?,?,?,?,?,?);
sql.cut(0) << "INSERT INTO " << table << " VALUES(";
for (i = 0; i < rd.NFields; i++)
{
if (i != 0) sql << ',';
sql << '?';
}
sql << ");";
sqlite3_stmt* pStatement = NULL;
int rc = sqlite3_prepare(_handle, sql, sql.len(), &pStatement, NULL);
const TRectype& curr = rel.curr();
for (cur = 0; cur.pos() < items; ++cur)
{
pi.addstatus(1);
if (pi.iscancelled())
break;
bind_record(curr, rd, pStatement); // Sostituisce i ? coi veri valori
rc = sqlite3_step(pStatement); // Ritorna sempre 101 (SQLITE_DONE)
rc = sqlite3_reset(pStatement); // Azzero lo statement per ricominciare
}
rc = sqlite3_finalize(pStatement);
exec("COMMIT"); // Fine transazione
}
// Creo gli indici DOPO l'importazione per maggiore velocita'
TProgind pi(rd.NKeys, TR("Creazione indici"), false, true);
for (int index = 0; index < rd.NKeys; index++)
{
pi.addstatus(1);
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);
}
set_dbf_time(table, last); // Aggiorna ora di ultima modifica
return true;
}
#else
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(";
int i;
for (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();
if (items > 0)
{
cur.freeze();
const TRectype& curr = rel.curr();
TString msg;
msg << TR("Esportazione tabella") << ' ' << table;
TPerformance_profiler prof(msg);
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;
}
#endif
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;
_first_row = 0;
_current_row = -1;
_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);
}
void TSQL_recordset::requery()
{
_items = 0;
_page.destroy();
}
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)
{
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 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);
}
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-32; // 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;
}
bool TRecordset::save_as_dbf(const char* table, int mode)
{
char volume[_MAX_DRIVE];
char dirname[_MAX_PATH];
char name[_MAX_FNAME];
char ext[_MAX_EXT];
xvt_fsys_parse_pathname (table, volume, dirname, name, ext, NULL);
const int logicnum = table2logic(name);
if (mode == 0x0)
mode = 0x1;
if (mode & 0x4)
{
if (logicnum >= LF_USER && *dirname == '\0')
{
TSystemisamfile isam(logicnum);
if (isam.zap() != NOERR)
return error_box(TR("Impossibile cancellare il file '%s'"), table);
}
else
{
TFilename n;
n = volume; n.add(dirname); n.add(name); n.ext("*");
TString_array files; list_files(n, files);
FOR_EACH_ARRAY_ROW(files, f, row)
xvt_fsys_removefile(*row);
}
mode = 0x1;
}
TLocalisamfile* pisam = NULL;
if (logicnum >= LF_USER)
{
if (*dirname)
pisam = new TIsamtempfile(logicnum, table);
else
pisam = new TLocalisamfile(logicnum);
}
if (pisam == NULL)
return error_box("Impossibile creare il file %s", table);
TLocalisamfile& isam = *pisam;
TProgind pi(items(), TR("Esportazione in corso..."));
TRectype& rec = isam.curr();
TString_array names;
int valid = 0;
for (unsigned int j = 0; j < columns(); j++)
{
const TVariant& var = get(j);
const TString& name = column_info(j)._name;
if (rec.exist(name))
{
names.add(name);
valid++;
}
else
names.add(EMPTY_STRING);
}
bool ok = true;
for (bool go = move_first(); go; go = move_next())
{
pi.addstatus(1);
rec.zero();
FOR_EACH_ARRAY_ROW(names, j, name) if (name->not_empty())
rec.put(*name, get(j).as_string());
int err = NOERR;
bool to_be_written = true;
// Devo solo aggiornare parte dei campi?
if ((mode & 0x2) && valid < rec.items())
{
err = isam.read(_isequal, _lock);
if (err != NOERR)
rec.zero();
FOR_EACH_ARRAY_ROW(names, j, name) if (name->not_empty())
rec.put(*name, get(j).as_string());
if (err == NOERR)
to_be_written = isam.rewrite() != NOERR;
}
if (to_be_written)
{
err = (mode & 0x1) ? isam.write() : isam.rewrite();
if (err != NOERR && (mode & 0x3) != 0)
err = (mode & 0x1) ? isam.rewrite() : isam.write();
}
if (err != NOERR)
{
ok = error_box(FR("Errore %d durante la scrittura del record %s"), err, rec.build_key());
break;
}
}
return ok;
}
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();
const TString& line();
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_select(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);
eatwhite(_instr);
if (_instr.eof())
return _token;
char instring = '\0';
while (true)
{
char c;
_instr.get(c);
if (c == EOF)
break;
if (instring > ' ') // Sono dentro ad una stringa
{
if (c == '\n')
break;
if (c == instring)
instring = '\0';
_token << c;
}
else
{
if (c == '"' || c == '\'')
{
instring = c;
_token << c;
}
else
{
if (isspace(c))
break;
_token << c;
}
}
}
}
return _token;
}
const TString& TCursor_parser::line()
{
if (_pushed.not_empty())
return pop();
char* buff = _token.get_buffer(256);
_instr.getline(buff, _token.size());
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_double_spaces();
se.replace(' ', sep);
se.separator(sep);
// Trasforma i nomi dei files in numeri se necessario
if (se.find('.') > 0)
{
TToken_string fld(16, '.');
TString16 name;
for (int i = 0; se.get(i, fld); i++)
{
if (!isdigit(fld[0]) && fld.find('.') > 0)
{
fld.get(0, name);
const int num = ::table2logic(name);
if (num != 0)
{
fld.add(num, 0);
se.add(fld, i);
}
}
}
}
}
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_select(TToken_string& filter)
{
filter = line();
filter.trim();
}
void TCursor_parser::parse_region(TRectype& rec)
{
TString16 field;
TString80 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_select(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;
int logicnum = table2logic(tok);
if (logicnum == LF_MAG && tok == "MAG") // Faccio prevalere la tabella MAG sul file MAG
logicnum = LF_TAB;
if (logicnum == LF_TAB || logicnum == LF_TABCOM)
_relation = new TRelation(tok);
else
_relation = new TRelation(logicnum);
add_column_info(tok, _relation->curr());
int key = 1; // key number
pop();
if (tok.starts_with("KE"))
{
pop();
key = atoi(tok);
}
else
push();
pop();
TToken_string filter;
if (tok.starts_with("FI") || tok.starts_with("SE"))
parse_select(filter);
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();
if (_cursor == NULL)
_cursor = new TCursor(_relation, "", key);
TRectype rec_start(_relation->curr());
TRectype rec_stop(rec_start);
while (true)
{
pop();
if (tok.starts_with("FR"))
parse_region(rec_start); else
if (tok.starts_with("TO"))
parse_region(rec_stop); else
if (tok.starts_with("JO"))
parse_join(); else
if (tok.starts_with("SO"))
parse_sortedjoin();
else
break;
}
push();
if (!rec_start.empty() || !rec_stop.empty())
_cursor->setregion(rec_start, rec_stop);
if (!filter.empty())
_cursor->setfilter(filter);
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
///////////////////////////////////////////////////////////
const TVariant& TISAM_recordset::get(int logic, const char* fldname) const
{
const int idx = relation()->log2ind(logic);
if (idx < 0)
return NULL_VARIANT;
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);
}
const TRectype& rec = relation()->file(idx).curr();
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
{
if (*name == '#')
return get_var(name);
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)
{
const TString& table = fldname.left(table_end);
logic = table2logic(table);
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];
}
TCursor* TISAM_recordset::cursor() const
{
if (_cursor == NULL && !_use.blank())
{
TString use; parsed_text(use);
TPerformance_profiler prof("ISAM query");
TISAM_recordset* my = (TISAM_recordset*)this;
#ifdef LINUX
string s(use.get_buffer());
istringstream instr(s);
#else
istrstream instr(use.get_buffer(), use.len());
#endif
TCursor_parser parser(instr, my->_column);
my->_relation = parser.get_relation();
my->_cursor = parser.get_cursor();
if (_cursor != NULL)
{
set_custom_filter(*_cursor);
const TRecnotype items = _cursor->items();
_cursor->freeze();
if (items > 0)
*_cursor = 0L;
}
}
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() : 0;
}
unsigned int TISAM_recordset::columns() const
{
cursor();
return _column.items();
}
bool TISAM_recordset::move_to(TRecnotype pos)
{
TCursor* c = cursor();
bool ok = c != NULL && pos >= 0;
if (ok)
{
*c = pos;
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;
}
}
void TISAM_recordset::requery()
{
if (_cursor != NULL)
{
delete _cursor;
_cursor = NULL;
}
}
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)
{
}