// $Id: relation.cpp,v 1.34 1995-04-10 15:27:51 guy Exp $ // relation.cpp // fv 12/8/93 // relation class for isam files #include #include #include #include #include #include #include #include #include #include #include // *** check if not already defined #define NOTFOUND (-1) // *** maximum number of elements in a cursor working page #define CMAXELPAGE 8000 #define print_name(out, f) out << (f.num() == LF_TABCOM ? "%" : "") << f.name() /////////////////////////////////////////////////////////// // TRelationdef /////////////////////////////////////////////////////////// class TRelationdef : public TObject { // @DPRIV 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; protected: virtual void print_on(ostream& out) const; void print_on(TToken_string& out) const; public: // @FPUB int num() const { return _num; } int link() const { return _numto; } int alias() const { return _alias; } bool write_enable() const { return _write_enable; } void write_enable(bool we) { _write_enable = we; } TRectype& load_rec(TRectype& r, const TBaseisamfile& from) const; const char* evaluate_expr(int j, const TLocalisamfile& to); TRelationdef(const TRelation* rel, int file, byte key, int linkto, const char* relexprs, int alias, bool allow_lock, bool write_enable = FALSE); virtual ~TRelationdef() {} }; // @END TRelationdef::TRelationdef(const TRelation* rel, int idx_file, byte key, int idx_to, const char* relexprs, int 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 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 = ""; print_name(out, f); out.add(prefix().description(f.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) { TExpression& expr = (TExpression&)_exprs[j]; for (int k = 0; k < expr.numvar(); k++) expr.setvar(k, to.get(expr.varname(k))); const char* val = expr; if (*val == '\0' && _altexprs.objptr(j)) { TExpression& altexpr = (TExpression&)_altexprs[j]; for (int k = 0; k < expr.numvar(); k++) altexpr.setvar(k, to.get(altexpr.varname(k))); val = altexpr; } return val; } /////////////////////////////////////////////////////////// // 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(TLocalisamfile* f) : _files(4) , _reldefs(4), _errors(NOERR) { _files.add(f); } 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::print_on(TArray& a) const { const TLocalisamfile& f = file(); TToken_string s(128); print_name(s, f); s.add(prefix().description(f.name())); s.add(""); s.add(f.getkey()); s.add(" | "); a.destroy(); a.add(s); for (int i = 0; i < _reldefs.items(); i++) { ((TRelationdef&)_reldefs[i]).print_on(s); a.add(s); } } void TRelation::restore_status() { _status.restart(); for (int i = 0; i < _files.items(); i++) { int err = _status.get_int(); int recno = _status.get_int(); if (recno >= 0l) file(i).readat(recno); else file(i).curr().zero(); 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++) { const int err = file(i).status(); const TRecnotype recno = file(i).eof() ? -1l : file(i).recno(); _status.add (err); _status.add (recno); } for (i = 0; i < _reldefs.items(); i++) { const 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(-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(int 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[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]; } 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(TLocalisamfile* f, const char* relexprs, int key, int linkto, int alias, bool allow_lock) { 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 _errors; } 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) { TTable* t = new TTable(tabname); return add(t, relexprs, key, linkto, alias, allow_lock); } void TRelation::replace(TLocalisamfile* f, int index) { 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); } TRectype& TRelationdef::load_rec(TRectype& r, const TBaseisamfile& from) const { r.zero(); for (int j = 0 ; j < _fields.items(); j++) // for each field { TFieldref& s = (TFieldref&) _fields[j]; s.write(s.read(from.curr()),r); } 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]; for (int k = 0; k < expr.numvar(); k++) expr.setvar(k, to.get(expr.varname(k))); */ const char* expr = rd.evaluate_expr(j, to); TFieldref& s = (TFieldref&) rd._fields[j]; s.write(expr, from.curr()); } // for each field } // read record: if not found, zero 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]) { TFieldref& fl = (TFieldref&)rd._fields[kk]; const TString f_fr(fl.read(from.curr())); eq = (f_fr == rd.evaluate_expr(kk, to)); } } 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]) { TFieldref& fl = (TFieldref&)reldef(j)._fields[kk]; const TString f_fr(fl.read(from.curr())); const TString 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, (TDate&)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; } #ifdef DBG 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, (TDate&)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 _errors = bad; } #endif 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); cond->setvar(i, r.get(s)); } return (bool) *cond; } FILE* TCursor::open_index(bool create) { #if XVT_OS == XVT_OS_SCOUNIX const char* const r = "r"; const char* const w = "w"; #else const char* const r = "rb"; const char* const w = "wb"; #endif if (create && _indexname.empty()) _indexname.temp("ci$$"); FILE* f = fopen(_indexname, create ? w : r); if (f == NULL) fatal_box("Can't use cursor index for file %d: '%s'\n", file().filehnd()->ln, (const char*)_indexname); 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(); FILE* _f = open_index(TRUE); if (file().filehnd()->i.Base[file().filehnd()->i.PN].PEOD == 0) { fclose(_f); return 0; } 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(); for (int i = 0; i < pagecnt; i++) { CRead(&file().filehnd()->f, rec.string(), page[i], _nolock); if ((_filterfunction ? _filterfunction(_if) : TRUE ) && (_fexpr ? __evalcondition(rec, _fexpr) : TRUE)) { if (np < i) page[np] = page[i]; np++; } } 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()) { key.ltrim(3); 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); #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() { main_app().begin_wait(); file().setkey(_nkey); file().read(_isgteq); const TRecnotype totrec = buildcursor(file().recno()); main_app().end_wait(); return totrec; } void TCursor::filter(const char* filter, const TRectype *from, const TRectype* to) { CHECK(!_frozen, "Impossibile filtrare un cursore congelato"); TString kf(_keyfrom), kto(_keyto), kfilter; if (filter) kfilter << filter; bool filterchanged = (filter != NULL) && (_filter != kfilter); if (file().tab()) { TTable& f = (TTable&) file(); kf = kto = f.name(); } if (from != NULL) { kf = from->key(_nkey); int p; while ((p = kf.find('~')) != -1) kf[p] = ' '; } if (to != NULL) { 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); const bool approx = (op == _isgteq); _if->file().read(op, lockop, atdate); if (approx) { while (_if->file().good() && !ok()) _if->file().next(); } const TRecnotype curpos = file().recno(); if (changed()) _totrec = update(); if (approx && _if->file().status() == _iseof) { _pos = _totrec - 1; return _pos; } 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); _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() { if (_indexname.not_empty()) ::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) fatal_box("Can't seek position %ld in cursor n. %d\n", _pos, file().filehnd()->ln); if (fread(&nrec, sizeof(TRecnotype), 1, _f) != 1) fatal_box("Can't read position %ld in cursor n. %d\n", _pos, file().filehnd()->ln); fclose(_f); curr().setdirty(); CRead(&file().filehnd()->f, curr().string(), nrec, _nolock); repos(); return nrec; } int TCursor::lock(TReclock l) { CLockRec(&file().filehnd()->f, _pos, l); return file().filehnd()->f.IOR; } TRecnotype TCursor::operator =(const TRecnotype pos) { if (changed()) _totrec = update(); CHECKD(pos >= 0 && pos <= _totrec, "Bad cursor position : ", pos); _pos = pos; readrec(); return _pos; } TRecnotype TCursor::operator +=(const TRecnotype npos) { if (changed()) _totrec = update(); _pos += npos; if (_pos > _totrec) _pos = _totrec; else if (_pos < 0) _pos = 0; readrec(); return _pos; } TRecnotype TCursor::items() { if (changed()) _totrec = 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)); } /////////////////////////////////////////////////////////// // TFieldRef /////////////////////////////////////////////////////////// int name2log(const char* name) { int log = 0; if (name && *name) { if (isdigit(*name) || *name == '-') { log = atoi(name); if (strchr(name, '@')) log = -log; } else { const int len = strlen(name); if (len == 3 || len == 4) log = TTable::name2log(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; } // 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; _from = atoi(s.mid(pos)); if (_from > 0) _from--; else _from = 0; par = s.find(',', pos); if (par > 0) _to = atoi(s.mid(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, TRUE); s = read(f.curr()); } else s = read(c->lfile(_id).curr()); return s; } void TFieldref::write(const char* val, TRelation* c) const { if (c == NULL) { #ifdef DBG yesnofatal_box("Guy consiglia di dare un'occhiatina al codice!"); #endif TLocalisamfile f(_fileid, TRUE); write(val, f.curr()); } else { TRectype &curr = c->lfile(_id).curr(); write(val, curr); } } void TFieldref::write(const char* val, TRectype& rec) const { if (_fileid >= CNF_GENERAL) return; if (_from > 0) { TString256 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; const TRectype r(_fileid); const int len = r.length(_name); return len - _from; } /////////////////////////////////////////////////////////// // TRecord_Array /////////////////////////////////////////////////////////// TRecord_array::TRecord_array(const TRectype& r, const char* numfield) : _file(r.num()), _num(numfield) { read(r); } TRecord_array::TRecord_array(int logicnum, const char* numfield) : _file(logicnum), _num(numfield) { } int TRecord_array::rec2row(const TRectype& r) const { CHECK(r == key(), "Incompatible record"); const int n = atoi(r.get(_num)); // Non e' detto che sia un int! CHECKD(n > 0 && n < 32000, "Bad line number in record ", n); return n; } int TRecord_array::add_row(const TRectype& r) { const int nr = rec2row(r); TRectype* o = (TRectype*)objptr(nr); if (o) *o = r; else add(r, nr); return nr; } bool TRecord_array::destroy_row(int r) { CHECKD(r > 0, "Can't destroy row ", r); return destroy(r); } void TRecord_array::destroy_rows() { const int u = last(); for (int i = 1; i <= u; i++) destroy_row(i); } bool TRecord_array::read(const TRectype& filter) { CHECKD(filter.num() == _file, "Bad key record ", filter.num()); CHECKS(filter.get(_num).empty(), "You can't specify in the filter the value ", (const char*)_num); destroy(); add(filter, 0); // Store filter record for later use TLocalisamfile f(_file); f.curr() = filter; for (int err = f.read(_isgteq); err == NOERR && f.curr() == filter; err = f.next()) add_row(f.curr()); return ok(); } bool TRecord_array::write(bool re) { int err = NOERR; TLocalisamfile f(_file); const int u = last(); for (int i = 1; i <= u; i++) { const TRectype* r = (TRectype*)objptr(i); if (r != NULL) { err = re ? f.rewrite(*r) : f.write(*r); if (err != NOERR); err = re ? f.write(*r) : f.rewrite(*r); if (err != NOERR) return error_box("Errore di scrittura della riga %d", i); } else { f.curr() = key(); f.put(_num, i); err = f.read(); if (err == NOERR) // La riga c'era ma ora non piu' { err = f.remove(); if (err != NOERR) return error_box("Errore di cancellazione della riga %d", i); } } } // Cancella eventuali residui successivi f.curr() = key(); f.put(_num, i); for (err = f.read(_isgteq); err == NOERR && f.curr() == key(); err = f.next()) { err = f.remove(); if (err != NOERR) { i = atoi(f.get(_num)); return error_box("Errore di cancellazione della riga %d", i); } } return TRUE; } bool TRecord_array::remove() { destroy_rows(); TLocalisamfile f(_file); f.curr() = key(); for (int err = f.read(_isgteq); err == NOERR && f.curr() == key(); err = f.next()) { err = f.remove(); return error_box("Errore di cancellazione delle righe"); } return TRUE; } // *** EOF relation.cpp