// $Id: relation.cpp,v 1.40 1995-05-09 09:12:24 villa Exp $ // relation.cpp // fv 12/8/93 // relation class for isam files #include #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() HIDDEN const char* field_type_str(TFieldtypes f) { const char* c = ""; switch(f) { case _realfld: c = "Reale"; break; case _datefld: c = "Data"; break; case _charfld: c = "Carattere"; break; case _boolfld: c = "Logico"; break; case _wordfld: case _intzerofld: case _longzerofld: case _intfld: case _longfld: c = "Intero"; break; default: break; } return c; } /////////////////////////////////////////////////////////// // 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; TString _description; 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); void set_description(const char* s) { _description = s; } 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); } // add description TString16 name = _rel->file(_num).name(); if (_rel->file(_num).tab()) { // tabella TDir dir; dir.get(TTable::name2log(name), _nolock, _nordir, _sysdirop); _description = dir.tab_des(name); } else _description = prefix().description(name); } 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(_description); 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::set_description(const char* d, int index) { if (index == -1) _main_desc = d == NULL ? "" : d; else reldef(index)._description = d == NULL ? "" : d; } 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); if (f.tab()) { // tabella TDir dir; dir.get(TTable::name2log(f.name()), _nolock, _nordir, _sysdirop); s.add(dir.tab_des(f.name())); } else 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* fil, const TRectype *from, const TRectype* to) { CHECK(!_frozen, "Impossibile filtrare un cursore congelato"); TString kf(_keyfrom), kto(_keyto); const bool filterchanged = (fil != NULL) && (_filter != fil); 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 = fil; 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; } _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* fil, int nkey, const TRectype *from, const TRectype* to) : _if(r), _nkey(nkey), _frozen(FALSE), _filterfunction(NULL), _fexpr(NULL) { file().setkey(_nkey); _pos = 0; _totrec = 0; _lastrec = 0; _lastkrec = 0; filter(fil, from, to); } 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, int first) : _file(r.num()), _num(numfield), _offset(first - 1) { read(r); } TRecord_array::TRecord_array(int logicnum, const char* numfield, int first) : _file(logicnum), _num(numfield), _offset(first - 1) {} const TRectype& TRecord_array::key() const { TRectype* r = (TRectype*)objptr(0); CHECK(r, "A TRecord_array lost its key"); return *r; } TRectype& TRecord_array::row(int n, bool create) { const int i = n > 0 ? n - _offset : -1; TRectype* r = (TRectype*)objptr(i); if (r == NULL && create) { r = new TRectype(key()); n = add(r, i) + _offset; // Riassegna n se era negativo! r->put(_num, n); // Aggiorna campo numero riga } CHECKD(r && n > 0, "Bad record number ", n); return *r; } bool TRecord_array::renum_key(const char* field, const TString& num) { CHECKS(!num.blank(), "Blank key value for field: ", field); TRectype* r = (TRectype*)objptr(0); if (r == NULL) { r = new TRectype(_file); r->zero(); add(r, 0); } const TString& curr = key().get(field); if (curr == num) return FALSE; for (int i = last(); i >= 0; i--) { r = (TRectype*)objptr(i); if (r) r->put(field, num); } return TRUE; } bool TRecord_array::renum_key(const char* field, long num) { TString16 n; n << num; return renum_key(field, n); } int TRecord_array::rec2row(const TRectype& r) const { CHECK(r == key(), "Incompatible record"); const int n = atoi(r.get(_num)) - _offset; // Non e' detto che sia un int! CHECKD(n > 0 && n < 32000, "Bad line number in record ", n + _offset); 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, bool pack) { CHECKD(r > _offset, "Can't destroy row ", r); const int index = r - _offset; const bool ok = destroy(index, pack); if (ok && pack) { for (int i = index; i < items(); i++) row(i + _offset, FALSE).put(_num, i + _offset); } return ok; } void TRecord_array::destroy_rows() { for (int i = last(); i > 0; i--) destroy(i); } int 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 field ", (const char*)_num); destroy(); add(filter, 0); // Store filter record for later use TLocalisamfile f(_file); f.curr() = key(); int err = f.read(_isgteq); for (int e = err; e == NOERR && f.curr() == key(); e = f.next()) add_row(f.curr()); return err; } int 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) break; } else { f.curr() = key(); f.put(_num, i + _offset); err = f.read(); if (err == NOERR) // La riga c'era ma ora non piu' { err = f.remove(); if (err != NOERR) break; } } } if (err == NOERR) { // Cancella eventuali residui successivi f.curr() = key(); f.put(_num, i + _offset); for (int e = f.read(_isgteq); e == NOERR && f.curr() == key(); e = f.next()) { err = f.remove(); if (err != NOERR) break; } } return err; } int TRecord_array::remove() { destroy_rows(); return rewrite(); } // TRelation_description void TRelation_description::read_rel() { int which_file; // scan files and build description arrays _rel->print_on(_files); for (int i = 0; i < _files.items(); i++) { TToken_string& tt = (TToken_string&)_files[i]; TString descfname(36); descfname << DESCDIR << "/d"; TString tn(tt.get(0)); if (tn[0] == '%' || tn[0] == '$') descfname << tn.mid(1); else descfname << tn; descfname << ".des"; if ((which_file = atoi(tn)) == 0) which_file = TTable::name2log(tn); if (fexist(descfname)) { 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); for (int f = 0; f < trec.fields(); f++) { ttmp = trec.fielddef(f); TString16 name = ttmp.get(0); TString80 dfld; if (!name.blank()) { dfld = conf.get(name); if (!dfld.blank() && dfld[0] != '#') { ttmp.add(dfld,4); // contiene: nome campo, tipo, lunghezza, // decimali, descrizione rdesc->add(ttmp); } } } if (rdesc->items() > 0) _fields.add(rdesc); else { _files.destroy(i); delete rdesc; } } else _files.destroy(i); } _files.pack(); } bool TRelation_description::choose_file (int file) { TArray_sheet sht(-1,-1,0,0,"Selezione archivio", "Descrizione archivio@70"); TToken_string tt(80); for (int i = 0; i < _files.items(); i++) { TToken_string& tf = _files.row(i); tt = tf.get(1); tt.cut(70); sht.add(tt); } if (sht.run() == K_ENTER) { _cur_file = (int)sht.selected(); _cfile = _files.row(_cur_file); return TRUE; } return FALSE; } bool TRelation_description::choose_field(const char* fld) { TArray_sheet sht(-1,-1,0,0,"Selezione campo", "Descrizione campo@50|Tipo@10|Dim."); TString_array& fd = (TString_array&)_fields[_cur_file]; TToken_string tt(80); for (int i = 0; i < fd.items(); i++) { TToken_string& tf = fd.row(i); tt = tf.get(4); tt.cut(50); tt.add(field_type_str((TFieldtypes)tf.get_int(1))); tt.add(tf.get(2)); sht.add(tt); } if (sht.run() == K_ENTER) { _cur_field = (int)sht.selected(); _cfield = fd.row(_cur_field); return TRUE; } return FALSE; } bool TRelation_description::build_menu(const char* title) { return FALSE; } bool TRelation_description::remove_menu() { return FALSE; } bool TRelation_description::is_table() { return _cfile.get_int(0) == 0; } int TRelation_description::file() { return _cfile.get_int(0); } const char* TRelation_description::table() { return _cfile.get(0); } const char* TRelation_description::file_desc() { return _cfile.get(1); } const char* TRelation_description::field_desc() { return _cfield.get(4); } const char* TRelation_description::field() { 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); } bool TRelation_description::set_field_description(int id, const char* field, const char* des) { for (int i = 0; i < _files.items(); i++) { TToken_string& tt = _files.row(i); if (tt.get_int(0) == id || tt.get_int(4) == id) break; } if (i == _files.items()) return FALSE; TString_array& fld = (TString_array&)_fields[i]; for (i = 0; i < fld.items(); i++) { TToken_string& tt = fld.row(i); if (strcmp(tt.get(0),field) == 0) { if (*des) tt.add(des,4); else { fld.destroy(i); fld.pack(); } break; } } return i < fld.items(); } bool TRelation_description::set_field_description(const char* id, const char* field, const char* des) { for (int i = 0; i < _files.items(); i++) { TToken_string& tt = _files.row(i); if (tt.get(0) == id) break; } if (i == _files.items()) return FALSE; TString_array& fld = (TString_array&)_fields[i]; for (i = 0; i < fld.items(); i++) { TToken_string& tt = fld.row(i); if (strcmp(tt.get(0),field) == 0) { if (*des) tt.add(des,4); else { fld.destroy(i); fld.pack(); } break; } } return i < fld.items(); } TRelation_description::TRelation_description(TRelation& r) : _rel(&r), _menu(FALSE), _cur_file(0), _cur_field(0), _cfile(80), _cfield(80) { read_rel(); if (_files.items() > 0) { _cfile = _files.row(0); _cfield = ((TString_array&)_fields[0]).row(0); } } TRelation_description::~TRelation_description() { if (_menu) remove_menu(); } // *** EOF relation.cpp