campo-sirio/include/relation.cpp

1312 lines
28 KiB
C++
Executable File

// $Id: relation.cpp,v 1.1.1.1 1994-08-12 10:52:01 alex Exp $
// relation.cpp
// fv 12/8/93
// relation class for isam files
#include <ctype.h>
#include <stdlib.h>
#include <expr.h>
#include <extcdecl.h>
#include <relation.h>
#include <tabutil.h>
#include <utility.h>
#include <xvtility.h>
#include <config.h>
#include <lffiles.h>
// *** check if not already defined
#define NOTFOUND (-1)
// *** maximum number of elements in a cursor working page
#define CMAXELPAGE 8000
///////////////////////////////////////////////////////////
// TRelationdef
///////////////////////////////////////////////////////////
class TRelationdef : public TObject
{
// @DPRIV
friend class TRelation;
const TRelation* _rel; // Relazione padre
int _num; // Posizione file
int _numto; // Posizione padre
byte _alias; // Alias
byte _key; // Chiave
TArray _fields; // Campi di join
TArray _exprs; // Condizioni di uguaglianza
TBit_array _forced;
bool _first_match; // primo match (ed esiste)
bool _allow_lock; // ?????
bool _write_enable;
protected:
virtual void print_on(ostream& out) const;
public:
// @FPUB
int num() const { return _num; }
int link() const { return _numto; }
byte alias() const { return _alias; }
bool& write_enable() { return _write_enable; }
TRectype& load_rec(TRectype& r, const TBaseisamfile* from) const;
TRelationdef(const TRelation* rel, int file, byte key,
int linkto, const char* relexprs, byte alias,
bool allow_lock, bool write_enable = FALSE);
virtual ~TRelationdef() {}
};
// @END
HIDDEN ostream& print_name(ostream& out, const TLocalisamfile* f)
{
const int logicnum = f->num();
if (logicnum == LF_TABCOM || logicnum == LF_TAB)
{
if (logicnum == LF_TABCOM) out << '%';
out << ((TTable*)f)->name();
}
else
out << logicnum;
return out;
}
TRelationdef::TRelationdef(const TRelation* rel, int idx_file, byte key,
int idx_to, const char* relexprs, byte alias,
bool allow_lock, bool write_enable)
: _num(idx_file), _key(key), _numto(idx_to),
_rel(rel), _fields(4), _exprs(4), _alias(alias),
_first_match(FALSE), _allow_lock(allow_lock),
_write_enable(write_enable)
{
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
TString80 n(s);
const int p = n.find('[');
if (p > 0) n.cut(p);
if (rel->file(_num)->curr().exist(n) == FALSE)
{
error_box("'%s' non e' un campo del file %d",
(const char*)n, rel->file(_num)->num());
continue;
}
#endif
if (r[eq+1] == '=')
{
_forced.set(i);
eq++;
}
const TString80 xx(s);
_fields.add(new TFieldref(xx,0));
_exprs.add(new TExpression(r.mid(eq+1), _strexpr));
}
}
void TRelationdef::print_on(ostream& out) const
{
out << "JOIN "; print_name(out, _rel->file(_num));
if (_numto > 0)
{
out << " TO ";
const int alias = _rel->reldef(_numto-1)->alias();
if (alias > 0) out << alias << '@';
else print_name(out, _rel->file(_numto));
}
if (_key > 1) out << " KEY " << (int)_key;
if (_alias > 0) out << " ALIAS " << (int)_alias;
for (int i = 0; i < _fields.items(); i++)
{
if (i == 0) out << " INTO";
out << ' ' << _fields[i] << '=' << _exprs[i];
}
}
///////////////////////////////////////////////////////////
// TRelation
///////////////////////////////////////////////////////////
TRelation::TRelation(int logicnum, bool linkrecinst)
: _files(4) , _reldefs(4), _errors(NOERR)
{
TLocalisamfile* f = new TLocalisamfile(logicnum, linkrecinst);
_files.add(f);
}
TRelation::TRelation(const char* tabname, bool linkrecinst)
: _files(4) , _reldefs(4), _errors(NOERR)
{
TTable* t = new TTable(tabname, linkrecinst);
_files.add(t);
}
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()
{
_status.restart();
for (int i = 0; i < _files.items(); i++)
{
int err = _status.get_int();
int recno = _status.get_int();
file(i)->readat(recno);
file(i)->setstatus(err);
}
for (i = 0; i < _reldefs.items(); i++)
{
bool first_match = _status.get_int ();
reldef(i)->_first_match = first_match;
}
}
void TRelation::save_status()
{
_status.cut(0);
for (int i = 0; i < _files.items(); i++)
{
int err = file(i)->status();
TRecnotype recno = file(i)->recno();
_status.add (format ("%d",err));
_status.add (format ("%d",recno));
}
for (i = 0; i < _reldefs.items(); i++)
{
bool first_match = reldef(i)->_first_match;
_status.add (format ("%d",first_match));
}
}
int TRelation::log2ind(int log) const
{
// returns _files index of logical number or
// NOTFOUND if not present
// sets error status
if (log < 0) return alias2ind(byte(-log));
const int num = _files.items();
if (log > 0)
{
for (int i = 0; i < num; i++)
if (file(i)->num() == log) return i;
}
return num ? 0 : NOTFOUND;
}
int TRelation::alias2ind(byte alias) const
{
if (alias < 1) 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;
}
int TRelation::name2ind(const char* name) const
{
const int num = name2log(name);
const int ind = log2ind(num);
return ind;
}
TLocalisamfile* TRelation::lfile(int logicnum) const
{
const int idx = log2ind(logicnum);
CHECKD(idx != NOTFOUND, "File not found n. ", logicnum);
return (TLocalisamfile*)_files.objptr(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.objptr(idx);
}
void TRelation::write_enable(int logicnum, const bool on)
{
if (logicnum == - 1)
{
for (int i = 0; i < _reldefs.items(); i++)
reldef(i)->write_enable() = on;
}
else
{
const int idx = log2ind(logicnum);
CHECKD(idx != NOTFOUND, "File not found n. ", logicnum);
reldef(idx)->write_enable() = on;
}
}
void TRelation::write_enable(const char* name, const bool on)
{
const int idx = name2ind(name);
CHECKS(idx != NOTFOUND, "File or Table not found:", name);
reldef(idx)->write_enable() = on;
}
bool TRelation::add(int logicnum, const char* relexprs, int key,
int linkto, byte alias, bool allow_lock)
{
const int idxto = log2ind(linkto);
if (idxto == NOTFOUND)
fatal_box("Can't join file %d to %d", logicnum, linkto);
int idx = log2ind(logicnum);
TLocalisamfile* f = new TLocalisamfile(logicnum, idx == NOTFOUND);
idx = _files.add(f);
if (relexprs && *relexprs)
{
TRelationdef* r = new TRelationdef(this, idx, key, idxto,
relexprs, alias, allow_lock);
_reldefs.add(r);
}
return _errors;
}
bool TRelation::add(const char* tabname, const char* relexprs, int key,
int linkto, byte alias, bool allow_lock)
{
// look for <to> file
const int idxto = log2ind(linkto);
if (idxto == NOTFOUND)
fatal_box("Can't link to file no. %d", linkto);
int idx = name2ind(tabname);
TTable* f = new TTable(tabname, idx == NOTFOUND);
idx = _files.add(f);
if (relexprs && *relexprs)
{
TRelationdef* r = new TRelationdef(this, idx, key, idxto,
relexprs, alias, allow_lock);
_reldefs.add(r);
}
return _errors;
}
TRectype& TRelationdef::load_rec(TRectype& r, const TBaseisamfile* from) const
{
r.zero();
for (int j = 0 ; j < _fields.items(); j++) // for each field
{
// TString& s = (TString&) _fields[j];
// r.put(s, from->get(s));
TFieldref& s = (TFieldref&) _fields[j];
s.write(s.read(from->curr()),r);
// r.put(s.name(), s.read(from->curr()));
}
return r;
}
void TRelation::zero()
{
for (int i = 0; i < _files.items(); i++)
file(i)->zero();
}
int TRelation::position_rels(TIsamop op, TReclock lockop,
TDate& atdate, int first)
{
_errors = NOERR;
// workhorse: position files for each active relation
for (int i = first; i < _reldefs.items(); i++)
{
TRelationdef& rd = *reldef(i);
TLocalisamfile* from = file(rd.num());
TLocalisamfile* to = file(rd.link());
TReclock lck = rd._allow_lock ? lockop : _nolock;
if (to->curr().empty())
{ from->zero(); continue; }
from->setkey(rd._key);
from->curr().zero();
// build record
if (rd._fields.items() && rd._exprs.items())
{
for (int j = 0 ; j < rd._fields.items(); j++) // for each field
{
TExpression& expr = (TExpression&)rd._exprs[j];
// setvar for every variable
for (int k = 0; k < expr.numvar(); k++)
expr.setvar(k, to->get(expr.varname(k)));
// eval expression and put result in field
TFieldref& s = (TFieldref&) rd._fields[j];
s.write(expr, from->curr());
// TString& s = (TString&) rd._fields[j];
// from->put(s, (const char*)expr);
} // for each field
}
// read record: if not found, empty current record
TRectype rec(from->curr());
from->read(op, lck, atdate);
if (from->bad())
{
rd._first_match = (from->curr() == rec);
if (rd._first_match)
{
bool eq = TRUE;
for (int kk = 0; eq && kk < rd._fields.items(); kk++)
{
if (rd._forced[kk])
{
// TString& fl = (TString&)rd._fields[kk];
// const TString f_fr(from->curr().get(fl));
TFieldref& fl = (TFieldref&)rd._fields[kk];
const TString f_fr(fl.read(from->curr()));
// const TFixed_string f_to(to->curr().get(fl));
// eq = (f_fr == f_to);
TExpression& expr = (TExpression&)rd._exprs[kk];
for (int k = 0; k < expr.numvar(); k++)
expr.setvar(k, to->get(expr.varname(k)));
eq = (f_fr == (const char*)expr);
}
}
if (eq) from->setstatus(NOERR);
else { rd._first_match = FALSE; from->curr().zero(); }
}
else from->curr().zero();
}
else rd._first_match = TRUE;
} // for each relation
return _errors;
}
bool TRelation::next_match(int logicnum, const char* fieldlist, int nkey)
{
if (logicnum == file()->num())
{
next();
return file()->good();
}
const int i = log2ind(logicnum);
CHECKD(i != NOTFOUND,"Nonexistent file referenced in relation ",logicnum);
for (int j = 0; j < _reldefs.items(); j++)
if (reldef(j)->num() == i) break;
TLocalisamfile* from = file(i);
TLocalisamfile* to = file(reldef(j)->link());
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->curr());
reldef(j)->load_rec(rec, from);
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])
{
// TString& fl = (TString&)reldef(j)->_fields[kk];
// const TFixed_string f_fr(from->curr().get(fl));
// const TFixed_string f_to(to->curr().get(fl));
TFieldref& fl = (TFieldref&)reldef(j)->_fields[kk];
const TFixed_string f_fr(fl.read(from->curr()));
const TFixed_string f_to(fl.read(to->curr()));
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);
for (const char* 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, botime, j+1);
reldef(j)->_first_match = FALSE;
return ok;
}
bool TRelation::is_first_match(int logicnum)
// TRUE se c'e' un record ed e' il primo match (non si e' mai fatta
// position_rels)
{
const int i = log2ind(logicnum);
CHECKD(i != NOTFOUND,"Nonexistent file referenced in relation ",logicnum);
for (int j = 0; j < _reldefs.items(); j++)
if (reldef(j)->num() == i) return reldef(j)->_first_match;
const int err = file()->status();
return err == NOERR;
}
bool TRelation::isconsistent(bool reset)
{
const int MAXREL = 24;
int bad = 0;
TRecnotype recnos[MAXREL];
for (int 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)
return _errors = bad;
position_rels(_isequal, _nolock, botime);
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 this->_errors = bad;
}
int TRelation::write(bool force, TDate& atdate)
{
_errors = NOERR;
if (file(0)->curr().empty())
return _errors; // *** no error returned **
// programmer must be aware
if ((_errors = file(0)->write(atdate)) != NOERR)
return _errors;
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;
int res = file(log)->write(atdate);
if (force && res == _isreinsert)
res = file(log)->rewrite(atdate);
if (_errors == NOERR) _errors = res;
}
return _errors;
}
int TRelation::rewrite(bool force, TDate& atdate)
{
_errors = file(0)->rewrite(atdate); // Riscrive testata
if (force && _errors == _iskeynotfound) // Se non la trova ...
_errors = file(0)->write(atdate); // ... forza la scrittura
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;
int res = file(log)->rewrite(atdate);
if (force && res == _iskeynotfound)
res = file(log)->write(atdate);
if (_errors == NOERR) _errors = res;
}
return _errors;
}
int TRelation::remove(TDate& atdate)
{
const int res = file(0)->remove(atdate);
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(atdate);
if (_errors == NOERR && res != _iskeynotfound) _errors = res;
}
return _errors;
}
///////////////////////////////////////////////////////////
// TCursor
///////////////////////////////////////////////////////////
HIDDEN bool __evalcondition(const TRectype& r,TExpression* cond)
{
for (int i = 0; i < cond->numvar(); i++)
{
const char* s = cond->varname(i);
// if (cond->type() == _numexpr)
// {
// real n(r.get(s));
// cond->setvar(i, n);
// }
// else
cond->setvar(i, r.get(s));
}
return (bool) *cond;
}
FILE* TCursor::open_index(bool create) const
{
#if XVT_OS == XVT_OS_SCOUNIX
const char* r = "r";
const char* w = "w";
#else
const char* r = "rb";
const char* w = "wb";
#endif
FILE* f = fopen(_indexname, create ? w : r);
if (f == NULL)
fatal_box("Can't use cursor index for file %d\n", file()->filehnd()->ln);
return f;
}
TRecnotype TCursor::buildcursor(TRecnotype rp)
{
TRecnotype ap = 0, junkl;
char s[82];
int junk, l, pagecnt = 0;
Page p;
int pos;
const int kl = file()->filehnd()->i.Base[file()->filehnd()->i.PN].KeyLen;
const bool filtered = has_filter();
if (file()->filehnd()->i.Base[file()->filehnd()->i.PN].PEOD == 0)
return 0;
FILE* _f = open_index(TRUE);
fseek(_f, 0L, SEEK_SET);
l = strlen(to());
BTrRead(&file()->filehnd()->i, (char*)(const char*) from(), s, &junkl, &junk);
if (junk == _iseof) return 0;
TRecnotype* page = new TRecnotype [CMAXELPAGE];
if (GetAPage(&file()->filehnd()->i, &p, file()->filehnd()->i.CurPag, &junk) != NOERR)
fatal_box("Can't read index n. %d - file n. %d", file()->filehnd()->i.PN + 1, file()->filehnd()->ln);
pos = file()->filehnd()->i.Pos - 1;
_pos = -1;
while (TRUE)
{
if (pos >= p.PA.PageHeader.NKey)
{
if (p.PA.PageHeader.Next == -1L) break;
file()->filehnd()->i.CurPag = p.PA.PageHeader.Next;
if (GetAPage(&file()->filehnd()->i, &p, file()->filehnd()->i.CurPag, &junk) != NOERR)
fatal_box("Can't read index n. %d - file n. %d", file()->filehnd()->i.PN + 1, file()->filehnd()->ln);
pos = 0;
}
const char* s0 = p.PA.AreaForKey + pos++ * (kl + 4);
if (l && (strncmp(to(), s0, l) < 0)) break;
if (pagecnt == CMAXELPAGE)
{
if (filtered) pagecnt = filtercursor(pagecnt, page);
fwrite(page, sizeof(junkl), pagecnt, _f);
if (pos == -1)
for (int i = 0; i < pagecnt; i++)
if (page[i] == rp)
{
_pos = ap + i;
break;
}
ap += pagecnt;
pagecnt = 0;
}
page[pagecnt++] = *((long *)(s0 + kl));
}
if (pagecnt)
{
if (filtered) pagecnt = filtercursor(pagecnt, page);
fwrite(page, sizeof(junkl), pagecnt, _f);
if (pos == -1)
for (int i = 0; i < pagecnt; i++)
if (page[i] == rp)
{
_pos = ap + i;
break;
}
ap += pagecnt;
}
if (_pos == -1 ) pos = 0;
delete page;
fclose(_f);
return ap;
}
int TCursor::filtercursor(int pagecnt, TRecnotype* page)
{
int np = 0;
TRectype& rec = file()->curr();
const bool tab = file()->tab();
if (tab) file()->filehnd()->r->Fd[0].RecOff += 3;
// TRecfield* codtab = tab ? new TRecfield(rec, "CODTAB") : NULL;
for (int i = 0; i < pagecnt; i++)
{
CRead(&file()->filehnd()->f, rec.string(), page[i], _nolock);
// if (tab) *codtab = (const char*)(*codtab) + 3;
if ((_filterfunction ? _filterfunction(_if) : TRUE ) &&
(_fexpr ? __evalcondition(rec, _fexpr) : TRUE))
{
if (np < i) page[np] = page[i];
np++;
}
}
if (tab) file()->filehnd()->r->Fd[0].RecOff -= 3;
// if (tab) delete codtab;
return np;
}
bool TCursor::ok() const
{
if (file()->bad()) return FALSE;
const TRectype& rec = file()->curr();
TString key(rec.key(_nkey)), kf(from()), kt(to());
if (file()->tab())
{
kf.ltrim(3);
kt.ltrim(3);
}
if (key < kf || (kt.not_empty() && kt < key.left(kt.len())))
return FALSE;
if ((_filterfunction ? _filterfunction(_if) : TRUE ) &&
(_fexpr ? __evalcondition(rec, _fexpr) : TRUE))
return TRUE;
return FALSE;
}
bool TCursor::changed()
{
isdef* fh = file()->filehnd();
if (_frozen && _lastrec != 0L) return _filename != fh->f.name;
#if XVT_OS==XVT_OS_SCOUNIX
const TRecnotype eod = file()->eod();
#else
int junk = 0;
const TRecnotype eod = cisgeteod(fh, &junk);
// GetHead(&fh->i, _nolock, &junk);
#endif
if (_lastrec != eod ||
(_lastkrec != fh->i.Base[_nkey -1].PEOD) ||
(_filename != fh->f.name))
{
_lastrec = eod;
_lastkrec = fh->i.Base[_nkey -1].PEOD;
_filename = fh->f.name;
return TRUE;
}
else return FALSE;
}
TRecnotype TCursor::update()
{
file()->setkey(_nkey);
file()->read(_isgteq);
const CURSOR old = get_cursor(TASK_WIN);
set_cursor(TASK_WIN, CURSOR_WAIT);
TRecnotype totrec = buildcursor(file()->recno());
// if (has_filter()) totrec = filtercursor();
set_cursor(TASK_WIN, old);
return totrec;
}
void TCursor::filter(const char* filter, TRectype *from, TRectype* to)
{
CHECK(!_frozen, "Impossibile filtrare un cursore congelato");
TString kf(_keyfrom), kto(_keyto), kfilter, tbpref;
if (filter)
kfilter << filter;
bool filterchanged = (filter != NULL) && (_filter != kfilter);
if (file()->tab())
{
TTable& f = (TTable&) *file();
tbpref = f.name();
}
if (from != NULL)
{
kf = tbpref;
kf << from->key(_nkey);
int p;
while ((p = kf.find('~')) != -1) kf[p] = ' ';
}
if (to != NULL)
{
kto = tbpref;
kto << to->key(_nkey);
int p;
while ((p = kto.find('~')) != -1) kto[p] = ' ';
}
if (filterchanged || (_keyfrom != kf) || (_keyto != kto))
{
_pos = 0;
_totrec = 0;
_lastrec = 0;
if (filterchanged)
{
_filter = kfilter;
if (_fexpr) delete _fexpr;
TTypeexp type = (_filter.find('"') != -1) ? _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;
}
file()->setkey(_nkey);
_keyfrom = kf;
_keyto = kto;
}
}
void TCursor::setkey(int nkey)
{
if (nkey != _nkey)
{
_lastrec = 0L;
_nkey = nkey;
file()->setkey(_nkey);
filter(NULL);
}
}
TRecnotype TCursor::read(TIsamop op, TReclock lockop, TDate& atdate)
{
TRecnotype *page;
int pagecnt;
file()->setkey(_nkey);
_if->file()->read(op, lockop, atdate);
const TRecnotype curpos = file()->recno();
if (changed())
_totrec = update();
FILE* _f = open_index();
if (fseek(_f, 0L, SEEK_SET) != 0)
fatal_box("Can't repos cursor : File %d\n", file()->filehnd()->ln);
page = new TRecnotype [CMAXELPAGE];
_pos = -1;
for (TRecnotype max = _totrec; _pos == -1 && max > 0; max -= pagecnt)
{
pagecnt = (max < CMAXELPAGE) ? (int)max : CMAXELPAGE;
fread(page, sizeof(TRecnotype), pagecnt, _f);
for (int i = 0; i < pagecnt; i++)
if (page[i] == curpos)
{
_pos = _totrec - max + i;
break;
}
}
if (_pos == -1) _pos = 0;
delete page;
fclose(_f);
readrec();
return _pos;
}
TCursor::TCursor(TRelation* r, const char* filter, int nkey, TRectype *from, TRectype* to)
: _frozen(FALSE), _filterfunction(NULL)
{
_if = r;
_nkey = nkey;
CHECKD(_nkey > 0 && _nkey <= file()->filehnd()->r->NKeys, "Bad key number : ", _nkey);
_indexname.temp("ci");
/*
#if XVT_OS==XVT_OS_SCOUNIX
_f = fopen(_indexname, "w+");
#else
_f = fopen(_indexname, "wb+");
#endif
if (_f == NULL)
fatal_box("Can't create cursor index on file %d: %s",
file()->num(), strerror(errno));
*/
FILE* _f = open_index(TRUE);
fclose(_f);
_pos = 0;
_totrec = 0;
_lastrec = 0;
_lastkrec = 0;
_filter << filter;
TTypeexp type = (_filter.find('"') != -1) ? _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;
file()->setkey(_nkey);
if (file()->tab())
{
TTable& f = (TTable&) *file();
_keyfrom = _keyto = f.name();
}
if (from != NULL)
{
_keyfrom << from->key(_nkey);
int p;
while ((p = _keyfrom.find('~')) != -1) _keyfrom[p] = ' ';
}
if (to != NULL)
{
_keyto << to->key(_nkey);
int p;
while ((p = _keyto.find('~')) != -1) _keyto[p] = ' ';
}
}
TCursor::~TCursor()
{
// fclose(_f);
::remove(_indexname);
if (_fexpr) delete _fexpr;
}
TRecnotype TCursor::readrec()
{
TRecnotype& nrec = file()->filehnd()->RecNo;
if (_pos == items())
{
file()->setstatus(_iseof);
curr().zero();
return nrec = 0L;
}
file()->setstatus(NOERR);
FILE* _f = open_index();
if ((fseek(_f, _pos * sizeof(TRecnotype), SEEK_SET) != 0) ||
(fread(&nrec, sizeof(TRecnotype), 1, _f) != 1))
fatal_box("Can't read record in file n. %d\n", file()->filehnd()->ln);
fclose(_f);
curr().setdirty();
CRead(&file()->filehnd()->f, curr().string(), nrec, _nolock);
if (file()->tab())
{
TRecfield codtab(curr(), "CODTAB");
codtab = (const char*)codtab + 3;
}
repos();
return nrec;
}
TRecnotype TCursor::operator =(const TRecnotype pos)
{
if (changed())
_totrec = update();
CHECKD(pos >= 0 && pos <= _totrec, "Bad cursor position : ", pos);
_pos = pos;
if (_pos > _totrec) _pos = _totrec;
readrec();
return _pos;
}
TCursor& TCursor::operator +=(const TRecnotype npos)
{
if (changed())
_totrec = update();
_pos += npos;
if (_pos > _totrec) _pos = _totrec;
readrec();
return *this;
}
TCursor& TCursor::operator -=(const TRecnotype npos)
{
if (changed())
_totrec = update();
_pos -= npos;
if (_pos < 0) _pos = 0;
readrec();
return *this;
}
TCursor& TCursor::operator ++()
{
if (changed())
_totrec = update();
_pos++;
if (_pos > _totrec) _pos = _totrec;
readrec();
return *this;
}
TCursor& TCursor::operator --()
{
if (changed())
_totrec = update();
if (_pos) _pos--;
readrec();
return *this;
}
TRecnotype TCursor::items()
{
if (changed())
_totrec = update();
return _totrec;
}
///////////////////////////////////////////////////////////
// TFieldRef
///////////////////////////////////////////////////////////
int name2log(const char* name)
{
int log = 0;
if (name && *name)
{
if (isdigit(*name))
{
log = atoi(name);
if (strchr(name, '@')) log = -log;
}
else
{
const int len = strlen(name);
if (len == 3 || len == 4)
log = TTable::name2log(name);
}
if (log == 0)
fatal_box("'%s' non e' un file o 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;
}
// A Fieldref should have the following format (only NAME is mandatory):
// FILE->NAME[FROM,TO]
TFieldref& TFieldref::operator =(const TString& s)
{
int pos = s.find("->");
if (pos > 0)
{
_id = s.left(pos); _id.strip(" ");
_fileid = name2log(_id);
pos += 2;
} else _fileid = pos = 0;
int par = s.find('[', pos);
_name = s.sub(pos, par); _name.strip(" ");
if (par > 0)
{
pos = par+1;
par = s.find(',', pos);
_from = atoi(&s[pos]);
if (_from > 0) _from--; else _from = 0;
if (par > 0) _to = atoi(&s[par+1]); else _to = -1;
}
else
{
_from = 0;
_to = -1;
}
return *this;
}
void TFieldref::print_on(ostream& out) const
{
if (_id.not_empty()) out << _id << "->";
out << _name;
if (_from > 0 || _to > 0)
{
out << '[' << (_from+1);
if (_to) out << ',' << _to;
out << ']';
}
}
const char* TFieldref::read(const TRectype& rec) const
{
static TString80 buffer;
if (_fileid >= CNF_GENERAL)
{
TToken_string s(_name, '.');
TConfig c(_fileid - CNF_STUDIO, s.get());
buffer = c.get(s.get());
}
else
{
buffer = rec.get(_name);
if (_from > 0 || _to > 0)
{
const int l = buffer.len();
const int from = (_from > l) ? l : _from;
const int to = (_to > l || _to < 1) ? l : _to;
if (to < l) buffer.cut(to);
if (from > 0) buffer.ltrim(from);
}
}
return buffer;
}
const char* TFieldref::read(const TRelation* c) const
{
const char * s;
if (c == NULL)
{
TLocalisamfile f(_fileid);
s = read(f.curr());
}
else
s = read(c->lfile(_id)->curr());
return s;
}
void TFieldref::write(const char* val, TRelation* c) const
{
TLocalisamfile* f = NULL;
TRectype* curr = NULL;
if (c == NULL)
{
f = new TLocalisamfile(_fileid);
curr = &f->curr();
}
else
curr = &c->lfile(_id)->curr();
write(val, *curr);
if (f != NULL) delete f;
}
void TFieldref::write(const char* val, TRectype& rec) const
{
if (_fileid >= CNF_GENERAL) return;
if (_from > 0)
{
TString80 campo(rec.get(_name));
campo.overwrite(val, _from);
rec.put(_name, campo);
}
else rec.put(_name, val);
}
int TFieldref::len(TRectype &rec) const
{
if (_to >= 0) return _to - _from;
if (_fileid == 0) return rec.length(_name) - _from;
TLocalisamfile f(_fileid);
const int len = f.curr().length(_name);
return len - _from;
}
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));
}
// *** EOF relation.cpp