campo-sirio/include/relation.cpp
guy cfe57d63b3 Patch level : 10.0
Files correlati     : ve1
Ricompilazione Demo : [ ]
Commento            :
Corretta gestione MESSAGE _TABLEREAD sulle tabelle di modulo


git-svn-id: svn://10.65.10.50/trunk@18729 c028cbd2-c16b-5b4b-a496-9718f37d4682
2009-04-17 14:13:47 +00:00

3063 lines
72 KiB
C++
Executable File
Raw Blame History

#include <config.h>
#include <diction.h>
#include <expr.h>
#include <extcdecl.h>
#include <prefix.h>
#include <progind.h>
#include <relation.h>
#include <sheet.h>
#include <sort.h>
#include <tabmod.h>
#include <tabutil.h>
#include <utility.h>
#include <codeb.h>
// *** check if not already defined
#define NOTFOUND (-1)
HIDDEN void print_name(ostream& out, const TLocalisamfile& f)
{
switch (f.num())
{
case LF_TABCOM: out << '%'; break;
case LF_TABMOD: out << '&'; break;
default: break;
}
out << f.name();
}
HIDDEN void print_name(TToken_string& out, const TLocalisamfile& f)
{
switch (f.num())
{
case LF_TABCOM: out << '%'; break;
case LF_TABMOD: out << '&'; break;
default: break;
}
out << f.name();
}
extern int get_error(int);
HIDDEN const char* field_type_str(TFieldtypes f)
{
const char* c = "";
switch(f)
{
case _charfld:
c = "Carattere"; break;
case _realfld:
c = "Reale"; break;
case _datefld:
c = "Data"; break;
case _boolfld:
c = "Logico"; break;
// case _wordfld:
// case _intzerofld:
// case _longzerofld:
// case _intfld:
// case _longfld:
default:
c = "Intero"; break;
}
return c;
}
///////////////////////////////////////////////////////////
// TRelationdef
///////////////////////////////////////////////////////////
class TRelationdef : public TObject
{
friend class TRelation;
const TRelation* _rel; // Relazione padre
int _num; // Posizione file
int _numto; // Posizione padre
int _alias; // Alias
byte _key; // Chiave
TArray _fields; // Campi di join
TArray _exprs; // Condizioni di uguaglianza
TArray _altexprs; // Condizioni di uguaglianza alternative
TBit_array _forced;
bool _first_match : 1; // primo match (ed esiste)
bool _allow_lock : 1; // ??
bool _write_enable : 1;
public: // TObject
virtual void print_on(ostream& out) const;
public:
int num() const { return _num; }
int link() const { return _numto; }
int alias() const { return _alias; }
bool allow_lock() const { return _allow_lock; }
bool write_enable() const { return _write_enable; }
void write_enable(bool we) { _write_enable = we; }
TRectype& load_rec(TRectype& r, const TRectype& from) const;
const char* evaluate_expr(int j, const TLocalisamfile& to);
void print_on(TToken_string& out) const;
TRelationdef(const TRelation* rel, int file, byte key,
int linkto, const char* relexprs, int alias,
bool allow_lock);
virtual ~TRelationdef() {}
};
TRelationdef::TRelationdef(const TRelation* rel, int idx_file, byte key,
int idx_to, const char* relexprs, int alias,
bool allow_lock)
: _rel(rel), _num(idx_file), _numto(idx_to),
_alias(alias), _key(key), _fields(4), _exprs(4),
_first_match(FALSE), _allow_lock(allow_lock),
_write_enable(FALSE)
{
TToken_string rels(relexprs);
int i = 0;
TString80 r,s;
for (const char* g = rels.get(); g; g = rels.get(), i++)
{
r = g;
int eq = r.find('=');
CHECKS(eq > 0, "Ahoo! E l'uguale 'ndo sta? ", (const char*)g);
s = r.left(eq); // Parte a sinistra dell' =
#ifdef DBG
const char* n = s;
const int p = s.find('[');
if (p > 0) n = s.left(p);
if (rel->file(_num).curr().exist(n) == FALSE)
{
yesnofatal_box("Errore di JOIN: '%s' non e' un campo del file %d",
n, rel->file(_num).num());
continue;
}
#endif
if (r[eq+1] == '=')
{
_forced.set(i);
eq++;
}
_fields.add(new TFieldref(s, 0));
s = r.mid(eq+1);
const int par = s.find('(');
if (par > 0)
{
_exprs.add(new TExpression(s.left(par), _strexpr), i);
_altexprs.add(new TExpression(s.sub(par+1, s.len()-1), _strexpr), i);
}
else
_exprs.add(new TExpression(s, _strexpr), i);
}
}
void TRelationdef::print_on(ostream& out) const
{
const TLocalisamfile& f = _rel->file(_num);
out << "JOIN ";
print_name(out, f);
if (_numto > 0)
{
out << " TO ";
const int alias = _rel->reldef(_numto-1).alias();
if (alias > 0) out << alias << '@';
else
{
const TLocalisamfile& t = _rel->file(_numto);
print_name(out, t);
}
}
if (_key > 1) out << " KEY " << (int)_key;
if (_alias > 0) out << " ALIAS " << _alias;
for (int i = 0; i < _fields.items(); i++)
{
if (i == 0) out << " INTO";
out << ' ' << _fields[i] << '=';
if (_forced[i]) out << '=';
out << _exprs[i];
if (_altexprs.objptr(i))
out << '(' << _altexprs[i] << ')';
}
}
void TRelationdef::print_on(TToken_string& out) const
{
const TLocalisamfile& f = _rel->file(_num);
out.cut(0);
print_name(out, f);
// add description
const char* name = f.name();
if (f.tab())
name = TDir::tab_des(name);
else
name = prefix().description(name);
out.add(name);
out << '|';
if (_numto > 0)
{
const int alias = _rel->reldef(_numto-1).alias();
if (alias > 0)
out << alias << '@';
else
{
const TLocalisamfile& t = _rel->file(_numto);
print_name(out, t);
}
} else out << ' ';
out.add(_key);
out.add(_alias);
out << '|';
for (int i = 0; i < _fields.items(); i++)
{
if (i) out << ' ';
out << _fields[i] << '=';
if (_forced[i]) out << '=';
out << _exprs[i];
if (_altexprs.objptr(i))
out << '(' << _altexprs[i] << ')';
}
}
const char* TRelationdef::evaluate_expr(int j, const TLocalisamfile& to)
{
const TRectype& rec = to.curr();
TExpression& expr = (TExpression&)_exprs[j];
for (int k = 0; k < expr.numvar(); k++)
{
const TFieldref fr(expr.varname(k), rec.num());
expr.setvar(k, fr.read(rec));
}
const char* val = (const char*)expr.as_string();
if (*val == '\0' && _altexprs.objptr(j))
{
TExpression& altexpr = (TExpression&)_altexprs[j];
for (int k = 0; k < altexpr.numvar(); k++)
{
const TFieldref fr(altexpr.varname(k), rec.num());
altexpr.setvar(k, fr.read(rec));
}
val = (const char*)altexpr.as_string();
}
return val;
}
///////////////////////////////////////////////////////////
// TRelation
///////////////////////////////////////////////////////////
TRelation::TRelation(int logicnum)
: _files(4), _reldefs(4), _errors(NOERR)
{
TLocalisamfile* f = new TLocalisamfile(logicnum);
_files.add(f);
}
TRelation::TRelation(const char* tabname)
: _files(4), _reldefs(4), _errors(NOERR)
{
TLocalisamfile* t = NULL;
if (tabname[0] == '&')
t = new TModule_table(tabname);
else
t = new TTable(tabname);
_files.add(t);
}
TRelation::TRelation(TLocalisamfile* l)
: _files(4), _reldefs(4), _errors(NOERR)
{ _files.add(l); }
TRelation::~TRelation()
{}
void TRelation::print_on(ostream& out) const
{
const TLocalisamfile& f = file();
out << "USE "; print_name(out, f);
const int k = f.getkey();
if (k > 1) out << " KEY " << k;
out << endl;
for (int r = 0; r < _reldefs.items(); r++)
out << _reldefs[r] << endl;
}
void TRelation::restore_status()
{
int i;
for (i = _files.last(); i >= 0 ; i--)
{
const int err = _status.get_int(i*3);
const TRecnotype recno = _status.get_long();
const int key = _status.get_int();
file(i).setkey(key);
if (recno >= 0l) file(i).readat(recno);
else file(i).zero();
file(i).setstatus(err);
}
_status.get(_files.items()*3 - 1); // mi riposiziono prima di tutti i first_match
for (i = 0; i < _reldefs.items(); i++)
{
const bool first_match = _status.get_int() ? TRUE : FALSE;
reldef(i)._first_match = first_match;
}
}
void TRelation::save_status()
{
_status.cut(0);
int i;
for (i = 0; i < _files.items(); i++)
{
const int err = file(i).status();
const TRecnotype recno = file(i).eof() ? -1l : file(i).recno();
const int key = file(i).getkey();
_status.add (err);
_status.add (recno);
_status.add (key);
}
for (i = 0; i < _reldefs.items(); i++)
{
const bool first_match = reldef(i)._first_match;
_status.add(first_match);
}
}
// @doc EXTERNAL
// @mfunc Ritorna l'indice di <p _files> del numero logico passato
//
// @rdesc Ritorna l'indice oppure NOTFOUND nel caso non sia presente
int TRelation::log2ind(
int log) const // @parm Numero logico del file di cui conoscere l'indice
// @comm Nel caso <p log> sia minore di 0 chiama la <mf TRelation::alias2ind>
//
// @xref <mf TRelation::alias2ind> <mf TRelation::name2ind>
{
// returns _files index of logical number or NOTFOUND if not present
if (log <= 0)
return alias2ind(-log);
const int nf = _files.items();
for (int i = 0; i < nf; i++)
if (file(i).num() == log)
return i;
return NOTFOUND;
}
// @doc EXTERNAL
// @mfunc Ritorna l'indice di <p _files> del alias del file passato
//
// @rdesc Ritorna l'indice oppure NOTFOUND nel caso non sia presente
int TRelation::alias2ind(
int alias) const // @parm Alias del file di cui conoscere l'indice
// @xref <mf TRelation::log2ind> <mf TRelation::name2ind>
{
if (alias <= 0) return 0;
for (int i = 0; i < _reldefs.items(); i++)
{
const TRelationdef& r = (const TRelationdef&)_reldefs[i];
if (r.alias() == alias) return r.num();
}
return NOTFOUND;
}
// @doc EXTERNAL
// @mfunc Ritorna l'indice di <p _files> del nome del file passato
//
// @rdesc Ritorna l'indice oppure NOTFOUND nel caso non sia presente
int TRelation::name2ind(
const char* name) const // @parm Nome del file di cui conoscere l'indice
// @xref <mf TRelation::alias2ind> <mf TRelation::alias2ind>
{
const int num = name2log(name);
const int ind = log2ind(num);
return ind;
}
// @doc EXTERNAL
// @mfunc Ritorna il descrittore del file
//
// @rdesc Ritorna il reference ad un <c TLocalisamfile> che indica il descrittore del
// file della relazione
TLocalisamfile& TRelation::lfile(
int logicnum) const // @parm Numero logico del file da ritornare (default 0)
// @parm const char* | name | Nome del file da ritornare
// @syntax TLocalisamfile& lfile(int logicnum)
// @syntax TLocalisamfile& lfile(const char* name)
//
// @comm E' comodo utilizzare anche l'operatore [] che richiama questa funzione con
// la prima sintassi.
{
const int idx = log2ind(logicnum);
CHECKD(idx != NOTFOUND, "Relation file not found n. ", logicnum);
return (TLocalisamfile&)_files[idx];
}
TLocalisamfile& TRelation::lfile(const char* name) const
{
const int idx = name2ind(name);
CHECKS(idx != NOTFOUND, "File or Table not found:", name);
return (TLocalisamfile&)_files[idx];
}
bool TRelation::has_children(int first) const
{
for (int i = first; i < _reldefs.items(); i++)
{
const TRelationdef& ref = reldef(i);
if (ref._numto == first)
return true;
}
return false;
}
// @doc EXTERNAL
// @mfunc Abilita/disabilita la scrittura sul file
void TRelation::write_enable(
int logicnum, // @parm Numero logico del file da abilitare/disabilitare (default 0)
// @parm cont char* | name | Nome del file da abilitare/disabilitare
const bool on) // @parm Indica l'operazione da effettuare sul file:
//
// @flag TRUE | Abilita la scrittura sul file (default)
// @flag FALSE | Disabilita la scrittura sul file
// @syntax void write_enable(int logicnum, const bool on)
// @syntax void write_enable(const char* name, const bool on)
//
// @comm Nel caso venga passato un numero logico uguale a 0 vengono abilitati/disabilitati tutti
// i file della relazione
{
if (logicnum == 0)
{
for (int i = 0; i < _reldefs.items(); i++)
reldef(i).write_enable(on);
}
else
{
const int idx = log2ind(logicnum);
CHECKD(idx > 0, "File not found n. ", logicnum);
reldef(idx-1).write_enable(on);
}
}
void TRelation::write_enable(const char* name, const bool on)
{
const int idx = name2ind(name);
CHECKS(idx > 0, "File or Table not found:", name);
reldef(idx-1).write_enable(on);
}
// @doc EXTERNAL
// @mfunc Aggiunge una nuovo file nella relazione
//
// @rdesc Ritorna se e' riuscito ad aggiungere il file alla relazione
bool TRelation::add(
TLocalisamfile* f, // @parm Descrittore del file da aggiungere
const char* relexprs, // @parm Espressione della relazione
int key, // @parm Chiave del file
int linkto, // @parm Posizione alla quale aggiungere il file
int alias, // @parm Alias da dare al file da aggiungere
bool allow_lock) // @parm Indica se fare il lock sul file oppure ignorarli
// @parm int | logicnum | Numero logico del file da aggiungere
// @parm const char* | tabname | Nome della tabella da aggiungere
// @syntax bool add(TLocalisamfile* f, const char* relexprs, int key, int linkto, int alias, bool allow_lock);
// @syntax bool add(int logicnum, const char* relexprs, int key, int linkto, int alias, bool allow_lock);
// @syntax bool add(const char* tabname, const char* relexprs, int key, int linkto, int alias, bool allow_lock);
// @comm Il parametro <p linkto> puo' assumere i valori:
//
// @flag 0 | Per il file principale
// @flag <gt>0 | Indica il numero logico del file
// @flag <lt>0 | Indica l'alias del file
{
const int idxto = log2ind(linkto);
if (idxto == NOTFOUND)
fatal_box("Can't join file %d to %d", f->num(), linkto);
const int idx = _files.add(f);
CHECK(relexprs && *relexprs, "Mancano le espressioni di collegamento");
TRelationdef* r = new TRelationdef(this, idx, key, idxto,
relexprs, alias, allow_lock);
_reldefs.add(r);
return TRUE;
}
bool TRelation::add(int logicnum, const char* relexprs, int key,
int linkto, int alias, bool allow_lock)
{
TLocalisamfile* f = new TLocalisamfile(logicnum);
return add(f, relexprs, key, linkto, alias, allow_lock);
}
bool TRelation::add(const char* tabname, const char* relexprs, int key,
int linkto, int alias, bool allow_lock)
{
TLocalisamfile* t = NULL;
if (*tabname == '&')
t = new TModule_table(tabname);
else
t = new TTable(tabname);
return add(t, relexprs, key, linkto, alias, allow_lock);
}
// @doc EXTERNAL
// @mfunc Sostituisce nella relazione un file
void TRelation::replace(
TLocalisamfile* f, // @parm Descrittore del file sostituto
int index, // @parm Posizione nel quale sostituire il file (default 0)
const char* relexprs, // @parm Nuova Espressione della relazione
int key) // @parm Nuova Chiave del file
{
const TLocalisamfile* p = (const TLocalisamfile*)_files.objptr(index);
CHECK(p && p->num() == f->num(), "Can't replace a file with different logic number");
_files.add(f, index);
if (relexprs && *relexprs)
{
TRelationdef* oldr=(TRelationdef*)_reldefs.objptr(index-1);
TRelationdef* r = new TRelationdef(this, index, key, oldr->link(),
relexprs, oldr->alias(), oldr->allow_lock());
_reldefs.add(r,index-1);
}
}
// @mfunc Sostituisce nella relazione un file
void TRelation::replacef(
TLocalisamfile* f, // @parm Descrittore del file sostituto
int lognum, // @parm Numero logico o alias
const char* relexprs, // @parm Nuova Espressione della relazione
int key) // @parm Nuova Chiave del file
{
int index=0;
if (lognum < 0) index=alias2ind(lognum);
if (lognum > 0) index=log2ind(lognum);
replace(f,index,relexprs,key);
}
TRectype& TRelationdef::load_rec(TRectype& r, const TRectype& from) const
{
r.zero();
TString80 val; // Fisso la lunghezza massima ad 80, devono essere campi chiave!
for (int j = 0 ; j < _fields.items(); j++) // for each field
{
const TFieldref& s = (const TFieldref&) _fields[j];
val = s.read(from);
s.write(val, r);
}
return r;
}
void TRelation::zero()
{
for (int i = 0; i < _files.items(); i++)
file(i).zero();
}
// @doc EXTERNAL
// @mfunc Permette di posizionare l'albero di relazioni tra i file
//
// @rdesc Ritorna il numero di errore avvenuto nell'opeerazione, NOERR nel caso
// venga eseguito tutto correttamente
int TRelation::position_rels(
TIsamop op, // @parm Operatore che esegue la relazione (default _isequal)
TReclock lockop, // @parm Tipo di lock sul file (default _nolock)
int first) // @parm Numero del file da cui costruire la relazione (default
// 0, cioe' dal file principale)
// @comm Viene eseguito tutto il lavoro di creazione delle relazione: se non
// trova un record adatto su un file, svuota il record corrente e non
// ritorna errore.
// <nl>Le altre funzioni sul record procedono normalmente
{
TString expr(80); // Stringa di lavoro per la valutazione delle espressioni
_errors = NOERR;
const int primo = first < 0 ? 0 : first;
const int ultimo = first < 0 && (-first) < _reldefs.items() ? -first : _reldefs.items();
// workhorse: position files for each active relation
for (int i = primo; i < ultimo; i++)
{
TRelationdef& rd = reldef(i);
if (primo > 0 && rd.link() < primo)
continue; // Inutile spostare file collegati a record precedenti
TLocalisamfile& from = file(rd.num());
TLocalisamfile& to = file(rd.link());
from.zero(); // Azzera il record corrente (tutti se TSortedfile)
if (to.curr().empty())
continue;
from.setkey(rd._key);
// build record
TRectype& furr = from.curr();
for (int j = 0; j < rd._fields.items(); j++) // for each field
{
expr = rd.evaluate_expr(j, to);
TFieldref& s = (TFieldref&) rd._fields[j];
s.write(expr, furr);
} // for each field
const TReclock lck = rd._allow_lock ? lockop : _nolock;
from.read(_isgteq, lck);
// record read : if not found, zero current record
bool eq = !from.bad();
for (int kk = 0; eq && kk < rd._fields.items(); kk++)
{
TFieldref& fl = (TFieldref&)rd._fields[kk];
TString80 f_fr(fl.read(furr));
TString80 f_ex(rd.evaluate_expr(kk, to));
if (rd._forced[kk])
eq = f_fr == f_ex;
else
{
f_fr.rtrim(); f_ex.rtrim();
eq = f_fr.compare(f_ex, f_ex.len()) == 0;
// Becca anche 000001=1
if (!eq && real::is_natural(f_fr) && real::is_natural(f_ex))
eq = atol(f_fr) == atol(f_ex);
}
}
rd._first_match = eq;
if (eq)
from.setstatus(NOERR);
else
furr.zero();
} // for each relation
return _errors;
}
// @doc EXTERNAL
// @mfunc Posiziona il numero logico sul record successivo
//
// @rdesc Ritorna se ha trovato la corrispondenza:
//
// @flag TRUE | Corrispondenza trovata
// @flag FALSE | Corrispondenza non trovata
bool TRelation::next_match(
int logicnum, // @parm Numero logico del file da fare avanzare
const char* fieldlist, // @parm Lista dei campi che devono rimanere costanti (default NULL)
int nkey) // @parm Numero della chiave (default 0)
// @comm Il posizionamento del numero logico non deve essere riferita al file
// principale; in ogni caso viene considerata consistente ad eccezione dei
// casi di inconsistenza nella prima posizione.
{
if (logicnum == file().num())
{
next();
return file().good();
}
const int i = log2ind(logicnum);
CHECKD(i != NOTFOUND,"Nonexistent file referenced in relation ",logicnum);
int j;
for (j = 0; j < _reldefs.items(); j++)
if (reldef(j).num() == i) break;
TLocalisamfile& from = file(i);
reldef(j)._first_match = FALSE;
if (from.bad())
return FALSE; // && vaffanculo();
const TRecnotype last = from.recno();
bool ok = TRUE; // Corrispondenza trovata ?
if (fieldlist == NULL)
{
TRectype rec(from.num());
reldef(j).load_rec(rec, from.curr());
from.setkey(reldef(j)._key);
from.next();
ok = (from.good() && from.curr() == rec);
for (int kk = 0; ok && kk < reldef(j)._fields.items(); kk++)
{
if (reldef(j)._forced[kk])
{
TFieldref& fl = (TFieldref&)reldef(j)._fields[kk];
const TString f_fr(fl.read(from.curr()));
const TString f_to(fl.read(rec));
ok = (f_fr == f_to);
}
}
if (ok) from.setstatus(NOERR);
}
else
{
if (nkey > 0)
from.setkey(nkey);
TToken_string fields(fieldlist);
TToken_string old(80);
const char * f;
for (f = fields.get(0); f; f = fields.get())
old.add(from.curr().get(f));
from.next();
old.restart();
for (f = fields.get(0); f && ok; f = fields.get())
{
const TFixed_string v(from.curr().get(f));
const char* o = old.get();
if (v != o)
ok = FALSE;
}
}
if (!ok)
{
from.readat(last);
return FALSE;
}
// Riposiziona gli eventuali files successivi
position_rels(_isequal, _nolock, j+1);
reldef(j)._first_match = FALSE;
return ok;
}
// @doc EXTERNAL
// @mfunc Controlla se c'e' un record ed e' il primo match (non si e' mai fatta
// <mf TRelation::position_rels>)
//
// @rdesc Ritorna TRUE se c'e' un record ed e' il primo match
bool TRelation::is_first_match(
int logicnum) // @parm Numero logico del file da controllare
{
const int i = log2ind(logicnum);
CHECKD(i != NOTFOUND,"Nonexistent file referenced in relation ",logicnum);
for (int j = 0; j < _reldefs.items(); j++)
{
TRelationdef& rdj = reldef(j);
if (rdj.num() == i)
return rdj._first_match;
}
const int err = file().status();
return err == NOERR;
}
#ifdef DBG
// @doc EXTERNAL
// @mfunc Controlla se la relazione e' corretta
//
// @rdesc Ritorna i seguent valori
//
// @flag TRUE | La relazione soddisfa i criteri di correttezza
// @flag FALSE | La relazione manca di almeno uno di criteri di correttezza
bool TRelation::isconsistent(
bool reset) // @parm Indica se cercare di riportare la relazione ad uno stato
// consistente (default FALSE)
// @comm Per essere corretta una relazione necessita che tutti i file siano
// corretti, che il record corrente non sia vuoto e che la relazione sia
// consistente
// <nl>Se <p reset> e' TRUE si cerca di riportare la relazione in uno stato
// consistente (basato sul record principale). Non viene fatto nessun altro
// controllo
// <nl>Questa funzione e' chiamata internamente da <mf TRelation::update>
// e <mf TRelation::remove>.
{
const int MAXREL = 24;
int bad = 0, i;
TRecnotype recnos[MAXREL];
for (i = 0; i < _files.items() && i < MAXREL; i++)
{
// must be file OK, non-empty record
bad |= file(i).good() || file(i).curr().empty();
recnos[i] = file(i).recno();
}
// if the above hold, check consistency
if (bad)
{
_errors = bad;
return FALSE;
}
position_rels(_isequal, _nolock);
for (i = 0; i < _files.items(); i++)
if (file(i).recno() != recnos[i])
{
bad = -1;
break;
}
if (reset == FALSE)
// leave as before
for(i = 0; i < _files.items(); i++)
file(i).readat(recnos[i]);
return TRUE;
}
#endif
int TRelation::write(bool force)
{
_errors = file(0).write();
if (_errors != NOERR)
return _errors;
for (int i = 0; i < _reldefs.items(); i++)
{
TRelationdef& rd = reldef(i);
TLocalisamfile& lf = file(rd.num());
if (!rd.write_enable() || lf.curr().empty())
continue;
int res = lf.write();
if (force && res == _isreinsert)
res = lf.rewrite();
if (_errors == NOERR)
_errors = res;
}
return _errors;
}
int TRelation::rewrite(bool force)
{
_errors = file(0).rewrite(); // Riscrive testata
if (force && _errors == _iskeynotfound) // Se non la trova ...
_errors = file(0).write(); // ... forza la scrittura
for (int i = 0; i < _reldefs.items(); i++)
{
TRelationdef& rd = reldef(i);
TLocalisamfile& lf = file(rd.num());
if (!rd.write_enable() || lf.curr().empty())
continue;
int res = lf.rewrite();
if (force && (res == _iskeynotfound || res == _iseof || res == _isemptyfile))
res = lf.write();
if (_errors == NOERR)
_errors = res;
}
return _errors;
}
int TRelation::remove()
{
const int res = file(0).remove();
if (_errors == NOERR && res != _iskeynotfound) _errors = res;
for (int i = 0; i < _reldefs.items(); i++)
{
TRelationdef& rd = reldef(i);
const int log = rd.num();
if (!rd.write_enable() ||
(file(log).curr()).empty()) continue;
const int res = file(log).remove();
if (_errors == NOERR && res != _iskeynotfound) _errors = res;
}
return _errors;
}
bool TRelation::exist(int logicnum) const
{
const bool lucky = log2ind(logicnum) >= 0;
return lucky;
}
///////////////////////////////////////////////////////////
// TCursor
///////////////////////////////////////////////////////////
HIDDEN bool __evalcondition(const TRelation& /* r */,TExpression* cond, const TArray& frefs)
{
for (int i = cond->numvar() - 1; i >= 0; i--)
{
const TRecfield* fr = (const TRecfield*)frefs.objptr(i);
if (fr)
cond->setvar(i, (const char*)*fr);
}
return cond->as_bool();
}
// @doc EXTERNAL
static TFilename _last_name;
static FILE* _last_ndx = NULL;
static bool _last_created = FALSE;
// @mfunc Apre il file di indice
//
// @rdesc Ritorna l'handle del file aperto
FILE* TCursor::open_index(
bool create) // @parm Indica se creare l'indice nel caso manchi (default FALSE)
{
_last_created = create;
if (_indexname.empty())
{
TString8 radix;
radix.format("c%d_", file().num());
_indexname.temp(radix);
}
if (_indexname != _last_name || create)
{
if (_last_ndx != NULL)
fclose(_last_ndx);
_last_ndx = fopen(_indexname, create ? "wb" : "rb");
if (_last_ndx == NULL)
fatal_box("Can't use cursor index for file %d: '%s'\n",
file().num(), (const char*)_indexname);
_last_name = _indexname;
}
return _last_ndx;
}
void TCursor::close_index(FILE* f)
{
CHECK(f == _last_ndx, "Bad open/close index sequence");
if (_last_created)
{
fclose(_last_ndx);
_last_ndx = NULL;
_last_name.cut(0);
_last_created = FALSE;
}
}
int TCursor::read_page(long page)
{
_pagefirstpos = page * _cmaxelpage;
CHECKD(_pagefirstpos < _totrec, "Bad cursor page ", page);
unsigned long elements = _totrec - _pagefirstpos;
if (elements > _cmaxelpage)
elements = _cmaxelpage;
const unsigned long startpos = sizeof(TRecnotype) * _pagefirstpos;
const unsigned long size = sizeof(TRecnotype) * elements;
FILE * indf = open_index();
fseek(indf, startpos, SEEK_SET);
if (fread(_page, size, 1, indf) != 1)
fatal_box("Can't read page number %d in cursor n. %d\n", page, file().num());
return (int) elements;
}
bool TCursor::has_simple_filter() const
{
bool yes = _filterfunction == NULL;
if (yes && _fexpr != NULL && _fexpr->numvar() > 0)
{
const RecDes& recd = curr().rec_des(); // Descrizione del record della testata
const KeyDes& kd = recd.Ky[_nkey-1]; // Elenco dei campi della chiave del cursore
for (int i = _fexpr->numvar()-1; yes && i >= 0; i--)
{
const char * vn = _fexpr->varname(i);
TFieldref f(vn, 0);
yes = f.file() == 0;
if (yes)
{
for (int k = kd.NkFields-1; k >= 0; k--)
{
const int nf = kd.FieldSeq[k] % MaxFields;
const RecFieldDes& rf = recd.Fd[nf];
yes = kd.FromCh[k] == 255 && f.name() == rf.Name;
if (yes) break;
}
}
}
}
return yes;
}
TRecnotype TCursor::buildcursor(TRecnotype rp)
{
_pagefirstpos = 0L;
memset(_page, 0, sizeof(TRecnotype)*_cmaxelpage);
_fpkey.destroy();
const int fhnd = file().handle();
if (DB_reccount(fhnd) == 0)
return 0;
TRecnotype oldrecno= RECORD_NON_FISICO,ap = 0;
int pagecnt = 0;
FILE* indf = NULL;
const int l = strlen(to());
int junk = DB_index_seek(fhnd, from());
if (junk < 0) junk=get_error(junk);
if (junk == _iseof) return 0;
// DB_index_recno(fhnd); // A cosa cavolo serve?
_pos=-1;
const bool filtered = has_filter();
const bool simple_filter = filtered && has_simple_filter();
while (!DB_index_eof(fhnd))
{
const char *s0 = DB_index_getkey(fhnd);
if (l && (strncmp(to(), s0, l) < 0))
break;
const TRecnotype recno = DB_index_recno(fhnd);
if (recno == oldrecno)
break; // means that no more keys are available
oldrecno=recno;
bool to_be_added = true;
if (filtered)
{
if (simple_filter)
to_be_added = simple_filtercursor(s0);
else
to_be_added = filtercursor(recno);
}
if (to_be_added)
{
_page[pagecnt++] = recno;
if (_pos < 0 && recno == rp)
_pos = ap;
ap++;
}
if (pagecnt == _cmaxelpage)
{
_fpkey.add(new TToken_string(s0));
pagecnt = 0;
if (indf == NULL)
{
indf = open_index(TRUE);
fseek(indf, 0L, SEEK_SET);
}
fwrite(_page,sizeof(TRecnotype)*_cmaxelpage, 1, indf);
}
DB_index_next(fhnd);
} // while
if (indf != NULL)
{
if (pagecnt > 0)
fwrite(_page,sizeof(TRecnotype)*pagecnt, 1, indf);
close_index(indf);
}
_pagefirstpos = ap > _cmaxelpage ? -(_cmaxelpage + 1) : 0L ;
return ap;
}
bool TCursor::call_filterfunction(TRecnotype recno) const
{
bool ok = true;
if (_filterfunction)
{
const int handle = file().handle();
// memorizzo la chiave prima di eventuali spostamenti
const TString256 s0 = DB_index_getkey(handle);
ok = _filterfunction(_if);
// ripristino la chiave dopo eventuali spostamenti
if (recno != DB_index_recno(handle))
DB_index_go(handle, s0, recno);
}
return ok;
}
bool TCursor::filtercursor(TRecnotype recno)
{
file().readat(recno);
if (update_relation())
{
// memorizzo la chiave prima di eventuali spostamenti
const TString256 s0 = DB_index_getkey(file().handle());
_if->update(-filter_limit());
// ripristino la chiave dopo eventuali spostamenti
const int handle = file().handle();
if (recno != DB_index_recno(handle))
DB_index_go(handle, s0, recno);
}
bool ok = call_filterfunction(recno);
if (ok && _fexpr)
ok = __evalcondition(*_if, _fexpr, _frefs);
return ok;
}
bool TCursor::simple_filtercursor(const char* key) const
{
if (_fexpr == NULL)
return true;
const RecDes& recd = curr().rec_des(); // Descrizione del record della testata
const KeyDes& kd = recd.Ky[_nkey-1]; // Elenco dei campi della chiave del cursore
TFieldref f;
for (int i = _fexpr->numvar()-1; i >= 0; i--)
{
f = _fexpr->varname(i);
int offset = 0;
for (int k = 0; k < kd.NkFields; k++)
{
const int nf = kd.FieldSeq[k] % MaxFields;
const RecFieldDes& rf = recd.Fd[nf];
int len = rf.Len;
if (f.name() == rf.Name)
{
offset += f.from();
if (f.to() > f.from())
len = f.to() - f.from();
else
len -= f.from();
char* val = (char*)(key+offset);
char oldchar = '\0';
int l;
for (l = len; l >= 0; l--) // rtrim
{
if (l == 0 || val[l-1] != ' ')
{
oldchar = val[l];
val[l] = '\0';
break;
}
}
switch (rf.TypeF)
{
case _boolfld:
_fexpr->setvar(i, strchr("1STXY", *val)!=NULL ? "X" : " ");
break;
case _intfld:
case _longfld:
{
const char* v;
for (v = val; *v == ' ' || *v == '0'; v++);
_fexpr->setvar(i, v);
}
break;
default:
_fexpr->setvar(i, val);
break;
}
if (oldchar)
val[l] = oldchar;
break;
}
offset += len;
}
}
return _fexpr->as_bool();
}
bool TCursor::ok() const
{
if (file().bad())
return FALSE;
const TRectype& rec = file().curr();
const TFixed_string key(rec.key(_nkey));
const TString& kf = from();
const TString& kt = to();
if (file().tab())
{
if (key.compare(kf, 3) < 0 || (kt.not_empty() && kt.compare(key, 3) < 0))
return FALSE;
}
else
{
if (key < kf || (kt.not_empty() && kt.compare(key, kt.len()) < 0))
return FALSE;
}
const TRecnotype old = file().recno();
if (update_relation())
{
_if->update();
if (DB_recno(file().handle()) != old)
file().readat(old);
}
if (call_filterfunction(old) &&
(_fexpr ? __evalcondition(*_if, _fexpr, _frefs) : true))
return true;
return false;
}
bool TCursor::changed()
{
if (_frozen && _lastrec > 0L)
return FALSE;
const TLocalisamfile& f = file();
if (prefix().get_dirtype(f.num()) == _nordir &&
_index_firm != prefix().get_codditta())
return TRUE;
const int handle = f.handle();
const TRecnotype eod = DB_reccount(handle);
if (_lastrec != eod ||
_lastkrec != DB_changed(handle))
return TRUE;
if (!f.curr().valid())
return TRUE;
return FALSE;
}
TRecnotype TCursor::update()
{
TWait_cursor hourglass;
TLocalisamfile& f = file();
f.setkey(_nkey);
if (f.curr().empty())
f.zero(); // Usare sempre l'azzeratore esperto del file non direttamente f.curr().zero()
f.read(_isgteq);
_totrec = buildcursor(f.recno());
const int handle = f.handle();
const TRecnotype eod = DB_reccount(handle);
_lastrec = eod;
_index_firm = prefix().get_codditta();
_lastkrec = DB_changed(handle);
return _totrec;
}
HIDDEN void matildator(const TRectype& oldrec, int key, bool tilde, TString& str)
{
/*
if (tilde)
{
TRectype rec(oldrec); // Utile duplicazione di record
const RecDes* recd = rec.rec_des(); // Descrizione del record della testata
const KeyDes& kd = recd->Ky[key-1]; // Elenco dei campi della chiave
TString80 val;
for (int i = kd.NkFields-1; i >= 0; i--)
{
const int nf = kd.FieldSeq[i] % MaxFields;
const RecFieldDes& rf = recd->Fd[nf];
val = rec.get(rf.Name);
if (val.not_empty())
{
if (rf.TypeF == _alfafld && val.len() < rf.Len)
{
val.left_just(rf.Len, '~');
rec.put(rf.Name, val);
}
break;
}
}
str = rec.key(key);
}
else
str = oldrec.key(key);
str.replace('~', ' ');
*/
str = oldrec.key(key);
if (tilde)
{
const RecDes& recd = oldrec.rec_des(); // Descrizione del record della testata
const KeyDes& kd = recd.Ky[key-1]; // Elenco dei campi della chiave
const int len = str.len();
const int nfields = kd.NkFields;
int foc = 0;
for (int i = 0; i < nfields; i++)
{
const int nf = kd.FieldSeq[i] % MaxFields;
const RecFieldDes& rf = recd.Fd[nf];
const int toc = foc + kd.FromCh[i] == 255 ? rf.Len : (kd.ToCh[i] - kd.FromCh[i] + 1);
if (toc >= len)
{
if (str[foc] > ' ' && rf.TypeF == _alfafld)
str.left_just(toc, ' ');
break;
}
foc = toc + 1;
}
}
str.replace('~', ' '); // non togliere posson arivare record con gia' le tildi
}
// @doc EXTERNAL
// @mfunc Setta il filtro sul cursore
void TCursor::filter(
const char* fil, // @parm Espressione del filtro
const TRectype* from, // @parm Primo record del pacchetto da esaminare
const TRectype* to, // @parm Ultimo record del pacchetto da esaminare
int tilde // @parm Flag per riempire le chiavi di ~ from(0x1) o to(0x2)
)
// @comm ATTENZIONE: non e' possibile filtrare un cursore congelato
{
TString kf(_keyfrom), kto(_keyto);
const bool filterchanged = (fil != NULL) && (_filter != fil);
if (from != NULL)
matildator(*from, _nkey, (tilde & 0x1) != 0, kf);
if (to != NULL)
matildator(*to, _nkey, (tilde & 0x2) != 0, kto);
if (kf[0] <= ' ' || kto[0] <= ' ')
{
switch (file().num())
{
case LF_TABGEN:
case LF_TABCOM:
case LF_TAB:
if (!real::is_natural(file().name())) // E' veramente una tabella o <20> un file normale?
{
kf.overwrite(file().name(), 0);
kto.overwrite(file().name(), 0);
};
break;
case LF_TABMOD:
if (!real::is_natural(file().name())) // E' veramente una tabella di modulo o <20> un file normale?
{
const TModule_table& t = (TModule_table&)file();
TString16 k;
k.format("%2s%6ld%3s", t.module(), t.customer(), t.name());
kf.overwrite(k, 0);
kto.overwrite(k, 0);
}
break;
default:
break;
}
}
if (filterchanged || (_keyfrom != kf) || (_keyto != kto))
{
CHECK(!frozen(), "Impossibile filtrare un cursore congelato");
_pos = 0;
_totrec = 0;
_lastrec = 0;
if (filterchanged)
{
_filter = fil;
if (_fexpr) delete _fexpr;
_frefs.destroy();
TTypeexp type = (_filter.find('"') >= 0) ? _strexpr : _numexpr;
if (_filter.not_empty())
{
_fexpr = new TExpression(_filter, type);
if (_fexpr->type() == _numexpr)
for (int i = 0 ; i < _fexpr->numvar(); i++)
if (file().curr().type(_fexpr->varname(i)) == _alfafld)
{
_fexpr->set_type(_strexpr);
break;
}
}
else _fexpr = NULL;
if (_fexpr)
{
const int items = _fexpr->numvar();
for (int i = 0 ; i < items; i++)
{
const char * vn = _fexpr->varname(i); //occhio
if (vn[0] != '#')
{
const TFieldref f(vn, 0);
const TString & id = f.id();
if (id.full())
{
_filter_update = true;
const int file_id = _if->name2ind(id);
if (_filter_limit < file_id)
_filter_limit = file_id;
}
_frefs.add(new TRecfield(_if->curr(f.file()), f.name(), f.from(), f.to()), i);
}
else
NFCHECK("Variabile strana %s", (const char*)vn);
}
}
}
_keyfrom = kf;
_keyto = kto;
}
}
void TCursor::setkey(int nkey)
{
if (nkey != _nkey)
{
_lastrec = 0L;
_nkey = nkey;
file().setkey(_nkey);
filter(NULL);
}
}
int TCursor::test(TIsamop op, TReclock lockop) const
{
TLocalisamfile& curfile = file();
const TRectype& currec = curfile.curr();
curfile.setkey(_nkey);
int err = NOERR;
if (op == _isequal)
{
const TString match(currec.key(_nkey));
bool trovato = FALSE;
for (err = curfile.read(op, lockop);
err == NOERR && match == currec.key(_nkey);
err = curfile.next(lockop))
{
if (ok())
{
trovato = TRUE;
break;
}
else
{
if (lockop != _nolock)
curfile.reread(_unlock);
}
}
if (!trovato && err == NOERR)
{
err = _iskeynotfound;
curfile.setstatus(err);
}
}
else
{
for (err = curfile.read(op, lockop);
err == NOERR;
err = curfile.next(lockop))
{
if (ok())
break;
else
{
//if (((TCursor *)this)->items() == 0)
if (_totrec == 0)
{
err = _isemptyfile;
break;
}
if (lockop != _nolock)
curfile.reread(_unlock);
const TString& kto = to();
if (kto.not_empty())
{
const TFixed_string curkey(currec.key(_nkey));
if (kto.compare(curkey, kto.len()) < 0)
{
err = _iseof;
break;
}
}
}
}
}
return err;
}
TRecnotype TCursor::read(TIsamop op, TReclock lockop)
{
TLocalisamfile& curfile = file();
if (changed())
{
const TRectype savecurr = curfile.curr();
update();
curfile.curr() = savecurr;
}
int err = test(op, lockop);
const TRecnotype curpos = curfile.recno();
TString match;
if (err == NOERR)
match = curfile.curr().key(_nkey);
if (changed())
{
NFCHECK("Com'e' possibile che ci sia nuovamente bisogno di update?");
update();
}
if (err != NOERR)
{
_pos = _totrec - 1;
if (_pos < 0)
{
curfile.zero();
err = _isemptyfile;
}
else
readrec();
curfile.setstatus(err);
}
else
{
const int pages = (_totrec-1) / _cmaxelpage + 1;
_pos = -1;
if (pages > 1)
{
// const TString match(curfile.curr().key(_nkey));
bool found = FALSE;
FOR_EACH_ARRAY_ROW(_fpkey, p , s)
{
if (match <= *s)
{
const int pagecnt = read_page(p);
found = TRUE;
for (int i = 0; i < pagecnt && _pos < 0L; i++)
if (_page[i] == curpos)
_pos = _pagefirstpos + i;
}
}
if (!found)
{
const int pagecnt = read_page(pages - 1);
for (int i = 0; i < pagecnt && _pos < 0L; i++)
if (_page[i] == curpos)
_pos = _pagefirstpos + i;
}
}
else
{
for (int i = 0; i < _cmaxelpage && _pos < 0L; i++)
if (_page[i] == curpos)
_pos = i;
}
if (_pos < 0L)
_pos = 0L;
readrec();
}
return _pos;
}
TCursor::TCursor(TRelation* r, const char* fil, int nkey,
const TRectype *from, const TRectype* to, int tilde)
: _if(r), _nkey(nkey), _fexpr(NULL), _frozen(false), _filter_update(false),
_filterfunction_update(false), _filter_limit(0), _filterfunction(NULL)
{
file().setkey(_nkey);
_pos = 0;
_totrec = 0;
_lastrec = 0;
_lastkrec = 0;
_pagefirstpos = 0L;
_page = new TRecnotype[_cmaxelpage];
memset(_page, 0, _cmaxelpage * sizeof(TRecnotype));
filter(fil, from, to, tilde);
}
TCursor::~TCursor()
{
if (_indexname.not_empty())
{
if (_indexname == _last_name && _last_ndx != NULL)
{
_last_created = TRUE; // Force close
close_index(_last_ndx);
}
::remove(_indexname);
}
delete _page;
if (_fexpr)
delete _fexpr;
}
TRecnotype TCursor::readrec()
{
TRecnotype nrec = 0L;
TLocalisamfile& f = file();
if (_pos >= items())
{
f.setstatus(_iseof);
f.zero();
return nrec;
}
f.setstatus(NOERR);
if (_pos < _pagefirstpos || _pos >= _pagefirstpos + _cmaxelpage)
read_page(_pos / _cmaxelpage);
nrec = _page[_pos - _pagefirstpos];
CHECKD(nrec >= 0, "Bad record position ", nrec);
curr().setdirty();
curr().readat(f, nrec);
repos();
return nrec;
}
// @doc EXTERNAL
// @mfunc Mette un lock sul record
//
// @rdesc Ritorna il numero di errore che si verifica nel porre il lock (NOERR) se
// non si verificano errori
int TCursor::lock(
TReclock l) // @parm Tipo di lock da porre sul record (vedi <t TReclock>)
{
int rt=NOERR;
switch(l)
{
case _lock:
rt=DB_lock_rec(file().handle(),_pos);
break;
case _unlock:
rt=DB_unlock(file().handle());
break;
default:
break;
}
if (rt != NOERR) rt=get_error(rt);
return(rt);
}
TRecnotype TCursor::operator =(const TRecnotype pos)
{
if (changed())
update();
CHECKD(pos >= 0 && pos <= _totrec, "Bad cursor position : ", pos);
_pos = pos;
readrec();
return _pos;
}
TRecnotype TCursor::operator +=(const TRecnotype npos)
{
if (changed())
update();
_pos += npos;
if (_pos > _totrec)
_pos = _totrec;
else
if (_pos < 0)
_pos = 0;
readrec();
return _pos;
}
TRecnotype TCursor::items()
{
if (changed())
update();
return _totrec;
}
bool TCursor::next_match(int lognum, const char* fl, int nk)
{
if (lognum == 0 || lognum == file().num())
{
++(*this);
return file().good();
}
else
return _if->next_match(lognum, fl, nk);
}
bool TCursor::is_first_match(int ln)
{
return (ln == 0 || ln == file().num()) ?
(_pos == 0 && file().good()) : (_if->is_first_match(ln));
}
bool TCursor::scan(CURSOR_SCAN_FUNC func, void* pJolly, const char* msg)
{
TRecnotype tot = 0; // Temporarily
{
TWait_cursor hourglass;
tot = items();
}
bool ok = true;
if (tot > 0)
{
TProgind* pi = NULL;
if (tot > 1)
{
if (msg == NULL || *msg == '\0')
msg = TR("Elaborazione in corso...");
pi = new TProgind(tot, msg, true, true);
}
freeze(true);
for (*this = 0; pos() < tot; ++*this)
{
if (!func(*relation(), pJolly))
{
ok = false;
break;
}
if (pi != NULL && !pi->addstatus(1))
{
ok = false;
break;
}
}
freeze(false);
if (pi != NULL)
delete pi;
}
return ok;
}
///////////////////////////////////////////////////////////
// TSorted_cursor
///////////////////////////////////////////////////////////
typedef struct
{
char f[256];
TRecnotype p;
} El_To_Sort;
// @doc EXTERNAL
// @mfunc Controlla la validita' dell'espressione
//
// @rdesc Ritorna se si tratta di una espressione valida
bool TSorted_cursor::check_expr(
TString& s) // @parm Espressione da controllare
// @comm Modifica l'espressione in modo da avere una valido parametro per TFieldref!
// <nl>Es. UPPER(20->RAGSOC[1,40]+) ==> 20->RAGSOC[1,40]+
// <nl>N.B.: la direzione dell'ordinamento va messa indicata sempre dopo la definizione del campo, mai
// dopo l'operatore UPPER(). E' a cura delle altre funzioni esterne che utilizzano la check_expr()
// discriminare il segno + o - che sia.
{
int p=s.find('(');
int p1=s.find(')');
bool rt=TRUE;
if (p>-1 && p1>-1)
if (s.find("UPPER") < 0)
{
error_box("Only UPPER() operator is allowed");
rt=FALSE;
}
else
{
s=s.mid(6,s.len()-7);
p=s.find('(');
p1=s.find(')');
if (p>-1 || p1 >-1)
{
error_box("Too many parentheses!");
rt=FALSE;
}
}
else
if ((p>-1 && p1==-1) || (p==-1 && p1>-1))
{
error_box("Unbalanced parentheses in expression");
rt=FALSE;
}
return rt;
}
bool TSorted_cursor::is_upper(TString& s)
// Controlla se nell'espressione esiste l'operatore UPPER()
{
bool rt=FALSE;
if (s.find("UPPER") >= 0)
rt = check_expr(s);
return rt;
}
TRecnotype TSorted_cursor::buildcursor(TRecnotype rp)
{
TRecnotype oldrecno=0,pos,ap = 0;
int abspos=0,junk, l, pagecnt = 0;
TString s;
FILE* _f = NULL;
setpagefirstpos(0L);
memset(page(), 0, sizeof(TRecnotype) * pagesize());
fpkey().destroy();
if (file().empty())
return 0;
TSort sort(sizeof(El_To_Sort));
_order_expr.restart();
while ((s=_order_expr.get()).not_empty())
{
check_expr(s); // Toglie l'eventuale operatore UPPER(), in modo che l'ultimo carattere
// indichi eventualmente la direzione dell'ordinamento
const char last = s.right(1)[0];
const char versus = (last=='-') ? 'd' : 'a';
if (last == '-' || last == '+')
s.rtrim(1);
TFieldref f(s,0);
// Il controllo del file e' automatico in f.len()
const TString & id = f.id();
const TRectype& r = relation()->lfile(id).curr();
const int flen = f.len(r);
if (!id.empty())
{
_sort_update = true;
const int file_id = relation()->name2ind(id);
if (filter_limit() < file_id)
set_filter_limit(file_id);
}
sort.addsortkey(abspos,flen,versus);
CHECKS(flen!=0,"Field can not have null length: ",(const char *) s);
abspos += flen;
CHECKD(abspos<=256, "Sort key too long: ", abspos);
}
const int handle = file().handle();
sort.init();
l = strlen(to());
junk=DB_index_seek(handle, (char*)(const char*) from());
if (junk < 0) junk=get_error(junk);
if (junk == _iseof) return 0;
pos = DB_index_recno(handle);
TCursor::pos()=-1;
while (!DB_index_eof(handle))
{
const char* s0 = DB_index_getkey(handle);
if (l && (strncmp(to(), s0, l) < 0))
break;
const TRecnotype recno = DB_index_recno(handle);
if (recno == oldrecno)
break; // means that no more keys are available
oldrecno=recno;
// Attenzione: la filtercursor non si si limita a filtrare ma avanza anche il cursore!!!!
const bool to_be_added = filtercursor(recno);
if (to_be_added)
{
El_To_Sort Element;
TFixed_string f(Element.f, sizeof(Element.f));
fill_sort_key(f);
Element.p=recno;
sort.sort((char *) &Element);
if (TCursor::pos() < 0 && recno == rp)
TCursor::pos() = ap;
ap++;
}
long rec = DB_index_next(handle);
if (rec < 0)
fatal_box("Can't read index n. %d - file n. %d",file().getkey(),file().num());
} // while
sort.endsort();
pagecnt = 0;
El_To_Sort* Element = NULL;
while ((Element=(El_To_Sort *)sort.retrieve()) != NULL)
{
page()[pagecnt++]=Element->p;
if (pagecnt == pagesize())
{
if (_f == NULL)
{
_f = open_index(TRUE);
fseek(_f, 0L, SEEK_SET);
}
fwrite(page(), sizeof(TRecnotype) * pagesize(), 1, _f);
pagecnt=0;
fpkey().add(new TToken_string(Element->f));
}
}
if (_f != NULL)
{
if (pagecnt > 0)
fwrite(page(),sizeof(TRecnotype) * pagecnt, 1, _f);
close_index(_f);
}
setpagefirstpos(ap > pagesize() ? -(pagesize() + 1) : 0L) ;
return ap;
}
const char* TSorted_cursor::fill_sort_key(TString& k)
{
TString sf;
k.cut(0);
for (TString80 s = _order_expr.get(0); s.not_empty(); s = _order_expr.get())
{
const bool is_up = is_upper(s);
const char last = s.right(1)[0];
if (last == '-' || last == '+')
s.rtrim(1);
const TFieldref f(s,0);
sf = f.read(*relation());
const TRectype& frec = curr(f.file());
TFieldtypes fld_type = frec.type(f.name());
int fld_len = f.len(frec);
// Converte in ANSI i campi data ed i sottocampi con una data!
if (fld_type == _datefld || (f.is_subfield() && TDate::isdate(sf)))
{
const TDate d(sf);
sf = d.string(ANSI);
fld_type = _datefld;
fld_len = 10;
}
else
{
if (is_up)
sf.upper();
}
TString8 fmt;
if (fld_type == _alfafld || fld_type == _datefld)
fmt.format("%%-%ds", fld_len);
else
fmt.format("%%%ds", fld_len);
k << format(fmt, (const char*)sf);
}
return k;
}
bool TSorted_cursor::changed()
{
bool rt = false;
if (_is_valid_expr)
{
rt = TCursor::changed();
if (!rt) rt=_is_changed_expr;
_is_changed_expr = FALSE;
}
else
NFCHECK("Can't perform changed() while sorted cursor expression is not valid!");
return rt;
}
TRecnotype TSorted_cursor::read(TIsamop op, TReclock lockop)
{
TString256 searching; fill_sort_key(searching);
searching.rtrim();
const int cmplen = searching.len();
TRecnotype first = 0L;
TRecnotype last = items()-1;
TRecnotype found = -1L;
FOR_EACH_ARRAY_ROW(fpkey(), i, s)
{
const int cmp = searching.compare(*s, cmplen);
if (cmp <= 0)
last = (i + 1) * pagesize() - 1;
else
first = i * pagesize();
}
const bool ghiacciato = !frozen();
if (ghiacciato) freeze(TRUE);
TString256 testing;
while (first <= last)
{
const TRecnotype test = (first+last)/2;
TCursor::operator=(test);
fill_sort_key(testing);
const int cmp = searching.compare(testing, cmplen);
if (cmp == 0)
{
if (op != _isgreat)
{
if (found < 0l || test < found)
found = test;
last = test-1;
}
else
first = test+1;
}
else
{
if (cmp < 0)
{
last = test-1;
if (op != _isequal)
{
if (found < 0l || test < found)
found = test;
}
}
else
first = test+1;
}
}
if (found >= 0L)
{
TCursor::operator=(found);
file().setstatus(NOERR);
if (lockop != _nolock)
lock(lockop);
}
else
{
found = items()-1;
if (found >= 0)
TCursor::operator=(found);
file().setstatus(op == _isequal ? _iskeynotfound : _iseof);
}
if (ghiacciato) freeze(FALSE);
return found;
}
void TSorted_cursor::change_order(const char* order_expr)
{
_sort_update = false;
if (order_expr && *order_expr && _order_expr != order_expr)
{
TString s;
_order_expr = order_expr;
_order_expr.restart();
while ((s=_order_expr.get()).not_empty() && (_is_valid_expr=check_expr(s))) ;
if (_is_valid_expr)
_is_changed_expr=TRUE;
}
}
TSorted_cursor::TSorted_cursor(TRelation *f, const char * order_expr, const char * filter, int key, const TRectype* from, const TRectype* to)
: TCursor(f,filter,key,from,to)
{
change_order(order_expr);
}
TSorted_cursor::~TSorted_cursor()
{
}
///////////////////////////////////////////////////////////
// TFieldRef
///////////////////////////////////////////////////////////
// @doc EXTERNAL
// @func Converte una stringa in numero logico o numero tabella
//
// @rdesc Ritorna il numero logico del file o il numero della tabella
int name2log(
const char* name) // @parm Stringa da convertire
{
int log = 0;
if (name)
{
while (isspace(*name))
name++;
if (*name)
{
log = table2logic(TFixed_string(name));
CHECKS(log != 0, "Non e' un file ne' una tabella: ", name);
}
}
return log;
}
TFieldref::TFieldref() : _fileid(0), _name(0) {}
TFieldref::TFieldref(const TString& s, short defid)
{
operator=(s);
if (_fileid == 0) _fileid = defid;
}
void TFieldref::copy(const TFieldref& f)
{
_fileid = f._fileid;
_id = f._id;
_name = f._name;
_from = f._from;
_to = f._to;
}
TObject* TFieldref::dup() const
{
TFieldref* f = new TFieldref(*this);
return f;
}
// @doc EXTERNAL
// @mfunc Operatore di assegnamento
//
// @rdesc Ritorna l'oggetto assegnato
TFieldref& TFieldref::operator =(
const TString& s) // @parm Stringa da assegnare all'oggetto
// @comm Un <c TFieldref> deve avere il seguente formato (solamente NAME e il mandante):
// <nl>FILE->NAME[FROM,TO]
{
int pos = s.find("->");
if (pos > 0)
{
_id = s.left(pos); _id.strip(" ");
_fileid = name2log(_id);
pos += 2;
}
else
{
pos = s.find('.');
if (pos > 0)
{
_id = s.left(pos); _id.strip(" ");
_fileid = name2log(_id);
pos++;
}
else
{
_id.cut(0);
_fileid = pos = 0;
}
}
int par = s.find('[', pos); // Cerca la fine del nome del campo
_name = s.sub(pos, par); // Estrae il nome del campo
_name.strip(" "); // Elimina eventuali spazi superflui
if (par > 0)
{
pos = par+1;
set_from(atoi(s.mid(pos)));
par = s.find(',', pos);
if (par > 0) set_to(atoi(s.mid(par+1)));
else set_to(from()+1);
}
else
{
set_from(0);
set_to(-1);
}
return *this;
}
void TFieldref::set_file(int f)
{
_id.cut(0);
if (f != 0)
{
_fileid = f;
_id << abs(f);
if (f < 0) _id << '@';
}
else _fileid = 0;
}
void TFieldref::print_on(ostream& out) const
{
if (_id.not_empty()) out << _id << "->";
out << _name;
if (_from > 0 || _to > 0)
{
out << '[' << (_from+1);
out << ',' << _to << ']';
}
}
static TString buffer;
const char* TFieldref::read(TConfig& ini, const char* defpar) const
{
ini.set_paragraph(_id.empty() ? defpar : (const char *) _id);
if (!ini.exist(_name))
return "";
buffer = ini.get(_name);
// Gestisce valori tra virgolette
const int l = buffer.len();
if (l > 1)
{
if ((buffer[0] == '"' || buffer[0] == '\'') && buffer[0] == buffer[l-1])
{
buffer.rtrim(1);
buffer.ltrim(1);
if (strchr(buffer, '\\'))
buffer = esc(buffer);
}
}
if (_from > 0 || _to > 0)
{
if (_to < l && _to > 0) buffer.cut(_to);
if (_from > 0) buffer.ltrim(_from);
}
return buffer;
}
static int quotes_needed(const char* val)
{
int yes = 0;
for ( ; *val; val++) if (strchr("\t\n\r\"' ", *val))
{
yes = 1;
if (*val < ' ')
{
yes = 2;
break;
}
}
return yes;
}
static void escapes(TString& val)
{
for (int i = 0; val[i]; i++)
{
if (val[i] == '\n')
{
val.insert(" ", i);
val[i++] = '\\';
val[i] = 'n';
} else
if (val[i] == '\r')
{
val.insert(" ", i);
val[i++] = '\\';
val[i] = 'r';
} else
if (val[i] == '\t')
{
val.insert(" ", i);
val[i++] = '\\';
val[i] = 't';
}
}
}
void TFieldref::write(TConfig& ini, const char* defpar, const char* val) const
{
if ((val == NULL || *val == '\0') && !ini.exist(_name))
return;
const char* para = _id.empty() ? defpar : (const char *) _id;
if (_from > 0 || _to > 0)
{
buffer = ini.get(_name, para);
if ((buffer[0] == '"' || buffer[0] == '\'') && buffer[0] == buffer[buffer.len()-1])
{
buffer.rtrim(1);
buffer.ltrim(1);
if (strchr(buffer, '\\'))
buffer = esc(buffer);
}
buffer.overwrite(val, _from);
val = buffer;
}
const int qn = quotes_needed(val); // Controlla se c'e' bisogno di virgolette
if (qn)
{
if (qn == 2)
{
buffer = val;
escapes(buffer);
buffer.insert("\"", 0);
buffer << '"';
}
else
{
const char* virg = strchr(val, '"') ? "'" : "\"";
buffer = val;
buffer.insert(virg, 0);
buffer << virg;
}
ini.set(_name, buffer, para);
}
else
ini.set(_name, val, para);
}
const char* TFieldref::read(const TRectype& rec) const
{
if (_fileid >= CNF_GENERAL)
{
TToken_string s(_name, '.');
TConfig c(_fileid - CNF_STUDIO, s.get(0));
buffer = c.get(s.get());
}
else
{
if (_from > 0 || _to > 0 || _name.find(':') > 0)
{
const TRecfield rf((TRectype&)rec, _name, _from, _to-1);
buffer = rf;
}
else
buffer = rec.get(_name);
}
return buffer;
}
const char* TFieldref::read(const TRelation& r) const
{
const TRectype& rec = _id.empty() ? r.curr() : r.lfile(_id).curr();
const char *s = read(rec);
return s;
}
void TFieldref::write(const char* val, TRelation& r) const
{
TRectype& rec = _id.empty() ? r.curr() : r.lfile(_id).curr();
write(val, rec);
}
void TFieldref::write(const char* val, TRectype& rec) const
{
if (_fileid >= CNF_GENERAL) return;
if (_from > 0 || _to > 0)
{
buffer = rec.get(_name);
if (_to <= _from)
((TFieldref*)this)->_to = rec.length(_name);
buffer.overwrite(val, _from, _to - _from);
rec.put(_name, buffer);
}
else
{
rec.put(_name, val);
}
}
int TFieldref::len(const TRectype &rec) const
{
int len = 0;
if (_to < _from)
{
if (_fileid > 0)
{
const RecDes& recd = prefix().get_recdes(_fileid);
const int p = findfld(&recd, _name);
len = p != FIELDERR ? recd.Fd[p].Len : 0;
}
else
len = rec.length(_name);
}
else
len = _to;
len -= _from;
return len;
}
///////////////////////////////////////////////////////////
// TRelation_description
///////////////////////////////////////////////////////////
void TRelation_description::init_files_array()
{
const TLocalisamfile& f = _rel->file();
TToken_string s(128);
print_name(s, f); // Logic number
const char* name = f.name();
if (f.tab())
name = TDir::tab_des(name);
else
name = prefix().description(name);
s.add(name); // Description
s.add(" "); // No join
s.add(f.getkey()); // Key
s.add(" | "); // No Alias nor expression
_files.destroy();
_files.add(s); // Main file
for (int i = 0; i < _rel->items(); i++)
{
_rel->reldef(i).print_on(s);
_files.add(s);
}
}
void TRelation_description::read_rel()
{
// scan files and build description arrays
init_files_array();
_fields.destroy();
for (int i = 0; i < _files.items(); i++)
{
TToken_string& tt = (TToken_string&)_files[i];
TFilename descfname; descfname << DESCDIR << "/d";
const char* tn = tt.get(0); // Codice del file o tabella
const int which_file = name2log(tn); // Numero logico del file
if (tn[0] == '%' || tn[0] == '$')
descfname << (tn+1);
else
descfname << tn;
descfname << ".des";
TConfig conf(descfname, DESCPAR);
// new record descriptor for _fields array
TString_array* rdesc = new TString_array;
TTrec trec; trec.get(which_file);
TToken_string ttmp(64);
TString dfld(256);
for (int f = 0; f < trec.fields(); f++)
{
ttmp = trec.fielddef(f);
const TString16 name(ttmp.get(0));
if (!name.blank())
{
dfld = conf.get(name, NULL, -1, "Missing description");
if (!dfld.blank() && dfld[0] != '#')
{
ttmp.add(dfld,4);
// contiene: nome campo, tipo, lunghezza, decimali, descrizione
rdesc->add(ttmp);
}
}
}
_fields.add(rdesc, i);
}
}
// @doc INTERNAL
// @mfunc Cambia la relazione descritta
void TRelation_description::change_relation(
TRelation& r, // @parm Nuova relazione da assegnare
TString_array& a) // @parm Array di descrittore dei file
{
_rel = &r;
read_rel();
if (a.items() > 0)
_files = a;
}
void TRelation_description::print_on(ostream& out) const
{
for (int i = 0; i < _files.items(); i++)
{
TToken_string& r = ((TRelation_description*)this)->_files.row(i);
TString16 cod(r.get(4));
if (atoi(cod) == 0)
cod = r.get(0);
else
cod << '@';
out << " " << cod << "->* ";
out << "\"" << r.get(1) << "\"\n";
}
}
// @doc INTERNAL
// @mfunc Seleziona un file
//
// @rdesc Ritorna il risultato dell'operazione:
//
// @flag TRUE | E' stato selzionato il file
// @flag FALSE | Non e' riuscito a selezionare il file
bool TRelation_description::choose_file(
int file) // @parm Numero logico del file da selezionare (default 0)
// @comm Dopo aver scelto un file (occorre che ritorni TRUE) i metodi della classe
// permettono di conoscere tutti i dati riguardanti i parametri del file selezionato
{
TArray_sheet sht(-1,-1,-4,-4,"Selezione archivio", "Codice|Descrizione archivio@70");
TToken_string tt(80);
int sel = 0;
for (int i = 0; i < _files.items(); i++)
{
TToken_string& tf = _files.row(i);
tt = tf.get(4);
int num = atoi(tt);
if (num == 0)
{
tt = tf.get(0);
num = atoi(tt);
if (sel == 0 && file > 0 && num == file)
sel = i;
}
else
{
if (sel == 0 && file < 0 && num == -file)
sel = i;
tt << '@';
}
tt.add(tf.get(1));
sht.add(tt);
}
sht.select(sel);
if (sht.run() == K_ENTER)
{
_cur_file = (int)sht.selected();
_cfile = _files.row(_cur_file);
return TRUE;
}
return FALSE;
}
// @doc INTERNAL
// @mfunc Seleziona un campo <p fld> del file selezionato
//
// @rdesc Ritorna il risultato dell'operazione:
//
// @flag TRUE | E' stato selzionato il campo
// @flag FALSE | Non e' riuscito a selezionare il campo
bool TRelation_description::choose_field(
const char* fld) // @parm Nome del campo da selezionare (default "")
// @comm Occorre che sia selezionato un file, diversamente viene considerato
// selezionato il primo file
{
TArray_sheet sht(-1,-1,76,20,"Selezione campo",
"Campo@10|Descrizione@50|Tipo@10|Dim.");
TString_array& fd = (TString_array&)_fields[_cur_file];
TToken_string tt(80);
int sel = 0;
for (int i = 0; i < fd.items(); i++)
{
TToken_string& tf = fd.row(i);
tt = tf.get(0);
if (sel == 0 && tt == fld)
sel = i;
tt.add(tf.get(4));
tt.add(field_type_str((TFieldtypes)tf.get_int(1)));
tt.add(tf.get(2));
sht.add(tt);
}
sht.select(sel);
if (sht.run() == K_ENTER)
{
_cur_field = (int)sht.selected();
_cfield = fd.row(_cur_field);
return TRUE;
}
return FALSE;
}
// @doc INTERNAL
// @mfunc Costruisce un menu
//
// @rdesc Ritorna solamente FALSE
bool TRelation_description::build_menu(
const char* title) // @parm Titolo del menu'
// @comm Costruisce un albero di menu' e setta il campo corrente quando viene
// selezionato (deve essere popup)
//
// @devnote Funzione non implementata.
{
return FALSE;
}
bool TRelation_description::remove_menu()
{
return FALSE;
}
bool TRelation_description::set_cur_file(int id)
{
const int n = _rel->log2ind(id);
if (n >= 0 && n != _cur_file)
{
_cur_file = n;
_cfile = _files.row(n);
}
return n >= 0;
}
int TRelation_description::file_num()
{
int n = -_cfile.get_int(4);
if (n == 0)
n = _cfile.get_int(0);
return n;
}
const char* TRelation_description::file_desc()
{
return _cfile.get(1);
}
void TRelation_description::file_desc(const char* desc)
{
_cfile.add(desc, 1);
_files.row(_cur_file) = _cfile;
}
const char* TRelation_description::field_desc()
{
return _cfield.get(4);
}
const char* TRelation_description::field_name()
{
return _cfield.get(0);
}
int TRelation_description::field_len()
{
return _cfield.get_int(2);
}
TFieldtypes TRelation_description::field_type()
{
return (TFieldtypes)_cfield.get_int(1);
}
// @doc INTERNAL
// @mfunc Setta la descrizione del campo
//
// @rdesc Ritorna se ha trovato il campo da settare
bool TRelation_description::set_field_description(
const char* field, // @parm Campo a cui assegnare la descrizione
const char* des) // @parm Descrizione da assegnare al campo
{
TString_array& fld = (TString_array&)_fields[_cur_file];
for (int i = 0; i < fld.items(); i++)
{
TToken_string& tt = fld.row(i);
if (strcmp(tt.get(0),field) == 0)
{
if (des && *des)
tt.add(des, 4);
else
{
fld.destroy(i);
fld.pack();
}
return TRUE;
}
}
if (des && *des)
{
TToken_string tt(80);
tt.add(field);
tt.add(des, 4);
fld.add(tt);
return TRUE;
}
return FALSE;
}
const char* TRelation_description::get_field_description(const char* field)
{
TString_array& fld = (TString_array&)_fields[_cur_file];
for (int i = fld.items()-1; i >= 0; i--)
{
TToken_string& tt = fld.row(i);
if (strcmp(tt.get(0),field) == 0)
return tt.get(4);
}
return "";
}
TRelation_description::TRelation_description(TRelation& r)
: _rel(&r), _cur_file(0), _cur_field(0), _menu(FALSE),
_cfile(80), _cfield(80)
{
read_rel();
if (_files.items() > 0)
{
_cfile = _files.row(0);
_cfield = "";
}
}
TRelation_description::~TRelation_description()
{
if (_menu) remove_menu();
}
////////////////////////////////////
// TSortedfile
////////////////////////////////////
// @mfunc Avanza di <p npos> record
int TSortedfile::operator +=(const TRecnotype npos)
{
int err = NOERR;
TRecnotype p = _curs->pos() + npos;
if (p < 0)
{
p = 0;
err = _isbof;
}
if (p >= _curs->items())
{
err = _iseof;
p = _curs->items();
}
*_curs = p;
setstatus(err);
return err;
}
// @mfunc Sposta indietro di <p npos> record
int TSortedfile::operator -=(const TRecnotype npos)
{
return operator+=(-npos);
}
// @mfunc Avanza al record successivo
int TSortedfile::operator ++()
{
return *this+=1;
}
// @mfunc Indietreggia al record precedente
int TSortedfile::operator --()
{
return *this-=1;
}
// @mfunc Si posiziona sul primo record del file (vedi <t TReclock>)
int TSortedfile::first(word lockop)
{
int err = _curs->items() > 0 ? NOERR : _iseof;
_curs->first_item();
setstatus(err);
return err;
}
// @mfunc Si posiziona sull'ultimo record del file (vedi <t TReclock>)
int TSortedfile::last(word lockop)
{
int err = _curs->items() > 0 ? NOERR : _iseof;
_curs->last_item();
setstatus(err);
return err;
}
// @mfunc Si posiziona sul successivo record del file (vedi <t TReclock>)
int TSortedfile::next(word lockop )
{
int err = _curs->pos() < _curs->items() ? NOERR : _iseof;
if (err == NOERR)
_curs->succ_item();
setstatus(err);
return err;
}
// @mfunc Si posiziona sul precedente record del file (vedi <t TReclock>)
int TSortedfile::prev(word lockop )
{
int err = _curs->pos() > 0 ? NOERR : _isbof;
if (err == NOERR)
_curs->pred_item();
setstatus(err);
return err;
}
// @mfunc Salta <p nrec> record dalla posizione corrente (vedi <t TReclock>)
int TSortedfile::skip(TRecnotype nrec, word lockop )
{
*_curs += nrec; // manca il lock...
setstatus(_curs->file().status());
NFCHECK("Operazione 'skip' non consentita sul cursore");
return 0;
}
// @mfunc Rilegge l'ultimo record letto (vedi <t TReclock>)
int TSortedfile::reread(word lockop )
{
return reread(curr(),lockop);
}
// @mfunc Rilegge l'ultimo record letto e lo copia in <p rec> (vedi <t TReclock>)
int TSortedfile::reread(TRectype& rec, word lockop)
{
return 0;
}
// @mfunc Legge il record (vedi <t TReclock> e <t TIsamop>)
int TSortedfile::read(word op , word lockop )
{
return read(curr(),op,lockop);
}
// @mfunc Legge il record e lo copia in <p rec> (vedi <t TReclock> e <t TIsamop>)
int TSortedfile::read(TRectype& rec, word op , word lockop )
{
if (&curr() != &rec)
curr() = rec;
_curs->read((TIsamop)op,(TReclock)lockop);
setstatus(_curs->file().status());
if (&curr() != &rec)
rec = curr();
return status();
}
// @mfunc Legge il record alla posizione <p nrec> e lo copia in <p rec> (vedi <t TReclock>)
int TSortedfile::readat(TRectype& rec, TRecnotype nrec, word lockop )
{
// read(rec,_isequal,lockop);
// !!!!!!!!!!!!!! attenzione !!!!!!!!!!!!!!
// non usare un sorted file come file principale di un sorted cursor;
// portare invece le espressioni del sorted file a livello di cursore
// altrimenti questa readat non sempre funziona , perche' viene chiamata con
// parametri presi dalle funzioni a basso livello sugli isam, quindi relativi alle posizioni fisiche
// e non a quanto restituito da recno() (che da' la posizione all'interno del cursore)
*_curs=nrec;
if (&curr() != &rec)
rec=curr();
setstatus(_curs->file().status());
return status();
}
// @mfunc Legge il record alla posizione <p nrec> e lo copia in <p rec> (vedi <t TReclock>)
int TSortedfile::readat(TRecnotype nrec, word lockop )
{
return readat(curr(),nrec, lockop );
}
// @mfunc Aggiunge un record
int TSortedfile::write()
{
return write(curr());
}
// @mfunc Aggiunge un record copiando da <p rec>
int TSortedfile::write(const TRectype& rec)
{
NFCHECK("Operazione 'write' non consentita sul TSortedfile");
return 0;
}
// @mfunc Riscrive un record
int TSortedfile::rewrite()
{
rewrite(curr());
return 0;
}
// @mfunc Riscrive un record (il record <p rec>)
int TSortedfile::rewrite(const TRectype& rec)
{
NFCHECK("Operazione 'rewrite' non consentita sul TSortedfile");
return 0;
}
// @mfunc Riscrive un record alla posizione <p nrec> copiando da <p rec>
int TSortedfile::rewriteat(TRecnotype nrec)
{
return rewriteat(curr(),nrec);
}
// @mfunc Riscrive un record alla posizione <p nrec> copiando da <p rec>
int TSortedfile::rewriteat(const TRectype& rec, TRecnotype nrec)
{
NFCHECK("Operazione 'rewriteat' non consentita sul TSortedfile");
return 0;
}
// @mfunc Elimina il record
int TSortedfile::remove()
{
return remove(curr());
}
// @mfunc Elimina il record copiando da <p rec>
int TSortedfile::remove(const TRectype& rec)
{
NFCHECK("Operazione 'remove' non consentita sul TSortedfile");
return 0;
}
// @mfunc Attiva la chiave <p nkey> sul file aperto
void TSortedfile::setkey(int nkey)
{
_curs->setkey(nkey);
}
// @mfunc Resetta la regione del file (chiama <p TCursor::setregion>
void TSortedfile::setregion(const TRectype &f, const TRectype &t, int tilde)
{
_curs->setregion(f, t, tilde);
}
// @mfunc
TRecnotype TSortedfile::eod() const
{
return _curs->items();
}
// @mfunc
bool TSortedfile::empty()
{
return _curs->items()==0;
}
TRectype& TSortedfile::curr() const
{
return _curs->curr();
}
void TSortedfile::set_curr(TRectype* curr)
{
_curs->file().set_curr(curr);
}
// @mfunc Costruttore.
TSortedfile::TSortedfile(int logicnum, TRelation* rel, const char* ordexpr, const char* filter, int nkey)
: TLocalisamfile(logicnum), _rel(NULL)
{
// costruisce il cursore
if (!rel)
_rel = new TRelation(logicnum);
else
_rel = rel;
_curs = new TSorted_cursor(_rel, ordexpr, "", nkey);
if (&curr()!=&(_curs->file().curr()))
_curs->file().set_curr(&curr());
_curs->setfilter(filter,TRUE); //must pass TRUE because cursors doesn't update rel (BUG).
// DON't move this line BEFORE set_curr : filter's fieldrefs are allocated at cursor setfilter
}
// @mfunc Distruttore
TSortedfile::~TSortedfile()
{
delete _curs;
if (_rel)
delete _rel;
}
//
// *** EOF relation.cpp