/* $Id: relation.h,v 1.31 1995-11-27 08:39:10 guy Exp $ */ // join.h // fv 12/8/93 // join class for isam files #ifndef __RELATION_H #define __RELATION_H #ifndef __ISAM_H #include #endif #ifndef __SORT_H class TSort; #endif class TRelation : public TObject { friend class TRelationdef; friend class TRelation_description; friend class TCursor; TToken_string _status; // stato della relazione TArray _files; // file descriptors TArray _reldefs; // TRelationdef array int _errors; protected: int log2ind(int logicnum) const; int alias2ind(int alias) const; int name2ind(const char* name) const; TRelationdef& reldef(int i) const { return (TRelationdef&)_reldefs[i]; } TLocalisamfile& file(int i = 0) const { return (TLocalisamfile&)_files[i]; } // position_rels fa tutto il lavoro: se non trova un record // adatto su un file, svuota il record corrente e non ritorna errore. // write etc. poi procedono normalmente int position_rels(TIsamop op = _isequal, TReclock lockop = _nolock, TDate& atdate = (TDate&)botime, int first = 0); public: // TObject virtual bool ok() const { return good(); } virtual void print_on(ostream& out) const; public: int update() { return position_rels(_isequal, _nolock);} void zero(); virtual int next(TReclock lockop = _nolock) { return file().next(lockop) == NOERR ? position_rels(_isequal, lockop) : file().status(); } virtual int prev(TReclock lockop = _nolock) { return file().prev(lockop) == NOERR ? position_rels(_isequal, lockop) : file().status(); } virtual int next(TDate& atdate) { return file().next(atdate) == NOERR ? position_rels(_isequal, _nolock, atdate) : file().status(); } virtual int prev(TDate& atdate) { return file().prev(atdate) == NOERR ? position_rels(_isequal, _nolock, atdate) : file().status(); } virtual int first(TReclock lockop = _nolock) { return file().first(lockop) == NOERR ? position_rels(_isequal, lockop) : file().status(); } virtual int last(TReclock lockop = _nolock) { return file().last(lockop) == NOERR ? position_rels(_isequal, lockop) : file().status(); } virtual int skip(TRecnotype nrec, TReclock lockop = _nolock) { return file().skip(nrec, lockop) == NOERR ? position_rels(_isequal, lockop) : file().status(); } virtual int read(TIsamop op = _isgteq, TReclock lockop = _nolock, TDate& atdate = (TDate&)botime) { return file().read(op, lockop, atdate) == NOERR ? position_rels(_isequal, lockop, atdate) : file().status();} TLocalisamfile& lfile(int logicnum = 0) const; TLocalisamfile& lfile(const char* name) const; TLocalisamfile& operator[] (int logicnum) const { return lfile(logicnum); } void write_enable(int logicnum = 0, const bool on = TRUE) ; void write_enable(const char* name, const bool on = TRUE) ; void write_disable(int logicnum = 0) { write_enable(logicnum, FALSE); } void write_disable(const char* name) { write_enable(name, FALSE); } TRectype& curr(int logicnum = 0) const { return lfile(logicnum).curr(); } // next_match for 0ne-to-many relations; positions logicnum (!= main) // on next matching record; returns TRUE or FALSE if no more matches; in // any case relation is kept consistent except when inconsistent in // first place bool next_match(int logicnum, const char* fieldlist = NULL, int nkey = 0); bool add(TLocalisamfile* f, const char* relexprs, int key, int linkto, int alias, bool allow_lock); bool add(int logicnum, const char* relexprs, int key = 1, int linkto = 0, int alias = 0, bool allow_lock = FALSE); bool add(const char* tabname, const char* relexprs, int key = 1, int linkto = 0, int alias = 0, bool allow_lock = FALSE); void replace(TLocalisamfile* f, int index = 0); // write methods virtual int write (bool force = TRUE, TDate& atdate = (TDate&)botime); virtual int rewrite(bool force = TRUE, TDate& atdate = (TDate&)botime); virtual int remove (TDate& atdate = (TDate&)botime); // status methods // return the status of the relation when called // with no args, or the status of the file when called with // a logical number bool eof( int logicnum = 0) const { return lfile(logicnum).eof(); } bool bof( int logicnum = 0) const { return lfile(logicnum).bof(); } bool status(int logicnum = 0) const { return lfile(logicnum).status(); } bool good( int logicnum = 0) const { return lfile(logicnum).good(); } bool bad( int logicnum = 0) const { return lfile(logicnum).bad(); } bool empty( int logicnum = 0) const { return lfile(logicnum).empty(); } // isconsistent() returns TRUE if every file in the relation is // OK, current record is non-empty, and relation is consistent. // If it's not and reset is TRUE, it tries to reset the relation // to a consistent state (based on main record) -- no further check // is done. // Also called internally by update and remove. bool isconsistent(bool reset = FALSE); // TRUE se c'e' un record ed e' il primo match (non si e' mai fatta // position_rels) bool is_first_match(int logicnum); // items() ritorna il numero di files collegati int items() { return _reldefs.items(); } void save_status () ; void restore_status () ; // positioning operators. return status TRecnotype operator +=(const TRecnotype npos) { return skip(npos); } TRecnotype operator -=(const TRecnotype npos) { return skip(-npos); } TRecnotype operator ++() { return next(); } TRecnotype operator --() { return prev(); } TRelation(int logicnum); TRelation(const char* tabname); TRelation(TLocalisamfile* f); virtual ~TRelation(); }; /////////////////////////////////////////////////////////// // TRecord_Array /////////////////////////////////////////////////////////// class TRecord_array : private TArray { int _file; // Numero logico del file principale int _offset; // Offset iniziale del record array TString16 _num; // Nome del campo col numero di riga protected: int rec2row(const TRectype& r) const; // Estrae il numero riga di un record const TString& num_field() const { return _num; } int remove_from(int i) const; bool good(const TRectype& rec) const; public: const TRectype& key() const; int rows() const { return items()-1; } // Numero di righe presenti int succ_row(int r) const { return succ(r - _offset) + _offset; } int pred_row(int r) const { return pred(r - _offset) + _offset; }; int last_row() const { return last() + _offset; } // Ultima riga int first_row() const { return succ_row(0); } // Prima riga const TRectype& row(int r) const // Riga r costante { CHECKD(r > _offset, "Bad record number ", r); return (const TRectype&)operator[](r - _offset); } bool exist(int r) const; // Controlla se esiste la riga r TRectype& row(int r, bool create); // Riga r con possibilita' di crearla virtual int add_row(TRectype* r); // Aggiungi/cambia una riga int add_row(const TRectype& r) { return add_row((TRectype*)r.dup()); } virtual bool destroy_row(int n, bool pack = FALSE); // Cancella una riga virtual bool destroy_row(const TRectype& r, bool pack = FALSE) { return destroy_row(rec2row(r), pack); } void destroy_rows(); // Cancella tutte le righe int logic_num() const { return _file; } void set_key(TRectype* r); // Cambia l'intera chiave (solo se vuoto!) bool renum_key(const char* field, const TString& num); bool renum_key(const char* field, long num); // Rinumera campo chiave in seguito a reinsert virtual int read(const TRectype& r); // Leggi tutto l'array da file virtual int read(TRectype* r); // Leggi tutto l'array da file virtual int write(bool re = FALSE) const; // Aggiorna il file virtual int rewrite() const { return write(TRUE); } virtual int remove() const; // Cancella tutti i record dal file TRecord_array(const TRectype& r, const char* numfield, int first = 1); TRecord_array(int logicnum, const char* numfield, int first = 1); TRecord_array(const TRecord_array& a); }; // Classe TCursor : public TObject class TExpression; typedef bool (*FILTERFUNCTION)(const TRelation* r); class TCursor : public TContainer { TRelation* _if; int _nkey; TRecnotype _pos; // Posizione corrente TRecnotype _totrec; TRecnotype _lastrec; TRecnotype _lastkrec; TFilename _filename; TString _filter; // Filtro TString _keyfrom; // chiave iniziale TString _keyto; // chiave finale TExpression* _fexpr; // Espressione relativo filtro bool _frozen; bool _filter_update; // Flag che permette l'update della relazione per l'espressione-filtro bool _filterfunction_update;// Flag che permette l'update della relazione per la funzione-filtro FILTERFUNCTION _filterfunction; TFilename _indexname; TRecnotype update(); protected: FILE* open_index(bool create = FALSE); virtual bool changed(); virtual TRecnotype buildcursor(TRecnotype rp); virtual int filtercursor(int pagecnt, TRecnotype* page); TRecnotype readrec(); void filter(const char* filter, const TRectype* from = NULL, const TRectype* to = NULL); bool update_relation() {return (_filter_update || _filterfunction_update);} public: // @FPUB TRecnotype operator =(const TRecnotype nr); // Va alla posizione nr TRecnotype operator +=(const TRecnotype nr); TRecnotype operator -=(const TRecnotype npos) { return operator +=(-npos); } TObject* first_item( ){ operator =( 0 ); return &curr( ); } TObject* succ_item( ){ operator +=( 1 ); return &curr( ); } TObject* pred_item( ){ operator -=( 1 ); return &curr( );} TObject* last_item( ){ operator =( items( ) -1 ); return &curr( ); } long objects( ) { return items( ); } TRecnotype& pos() { return _pos; } TRecnotype items(); TRecnotype size() const { return file().eod(); } const TString& from() const { return _keyfrom; } const TString& to() const { return _keyto; } TRectype& curr(int log = 0) const { return _if->curr(log); } TRectype& curr(const char * tab) const { return _if->lfile(tab).curr(); } TRecnotype read(TIsamop op = _isgteq, TReclock lockop = _nolock, TDate& atdate = (TDate&)botime); int lock(TReclock = _lock); int unlock() { return lock(_unlock); } virtual bool ok() const; const char* filter() const { return _filter; } void freeze(bool on = TRUE) { _frozen = on; } bool frozen() const { return _frozen; } void setfilter(const char* filter_expr, bool update=FALSE) { filter(filter_expr); _filter_update = update; } void setregion(const TRectype& from, const TRectype& to) { filter(NULL,&from, &to); } TRelation* relation() const { return _if; } TLocalisamfile& file(int lnum = 0) const { return _if->lfile(lnum); } TLocalisamfile& file(const char* name) const { return _if->lfile(name); } int repos() { return _if->position_rels(); } TExpression* expression() const { return _fexpr; } FILTERFUNCTION filterfunction() const { return _filterfunction; } void setkey() { file().setkey(_nkey); } void setkey(int nkey); int key() const { return _nkey; } bool next_match(int lognum, const char* fl = NULL, int nk = 0); bool is_first_match(int ln); void set_filterfunction(FILTERFUNCTION ff, bool update=FALSE) { _filterfunction = ff; _lastrec = 0L; _filterfunction_update = update;} bool has_filter() const { return _filter.not_empty() || _filterfunction; } void save_status () { _if->save_status(); } void restore_status () { _if->restore_status(); } TCursor(TRelation* f, const char* filter = "", int key = 1, const TRectype* from = NULL, const TRectype* to = NULL); virtual ~TCursor(); }; // Classe TSorted_cursor. Costruisce e gestisce un cursore ordinato per chiavi diverse da quelle specificate nel file. // Il formato dell'espressione deve essere: [LF->]FIELDNAME[[from,to]][+|-]... Inoltre tutta l'espressione puo' essere racchiusa // dall'operatore UPPER(), per controlli non case-sensitive. // FIELDNAME e' il nome del campo per quale si vuole effettuare il sort, LF indica il numero logico del file(appartenente alla relazione!). // from e to sono parametri opzionali usati per specificare un sottocampo. Per default prende l'intera lunghezza. // + e - sono parametri opzionali usati per specificare il tipo di ordinamento: + significa crescente, - significa // decrescente. Per default l'ordinamento e' +(crescente). E' cosi' possibile creare chiavi con campi non appartententi al // file principale della relazione. // Es. "CODCF-|TIPOCF|SOTTOCONTO+|UPPER(20->RAGSOC[1,40]-)" class TSorted_cursor : public TCursor { TToken_string _order_expr; TSort *_sort; bool _is_changed_expr; bool _is_valid_expr; protected: bool check_expr(TString& s); // Controlla il formato della singola espressione di un campo di sort bool is_upper(TString& s); // Controlla se la singola espressione contiene l'operatore UPPER(), ritornandone l'argomento virtual TRecnotype buildcursor(TRecnotype rp); // Vedi i TCursor virtual int filtercursor(int pagecnt, TRecnotype* page); // Vedi i TCursor virtual bool changed(); // Vedi i TCursor public: TRecnotype operator =(const TRecnotype nr) {return TCursor::operator =(nr); } void change_order(const char * order_expr); // Permette di cambiare l'ordinamento del cursore. TSorted_cursor(TRelation *f,const char * order_expr, const char * filter = "", int key = 1, const TRectype* from = NULL, const TRectype* to = NULL); virtual ~TSorted_cursor(); }; // Classe TFieldref : public TObject class TFieldref : public TObject { short _fileid; // Numero del file TString _id; // Nome tabella o stringa col numero del file TString _name; // Nome del campo int _from, _to; // Substring protected: virtual void print_on(ostream& out) const; public: TFieldref(); TFieldref(const TString&, short defid); TFieldref& operator =(const TString& s); // Operatore di assegnazione virtual bool ok() const { return _name.not_empty(); } // Vero se il numero del file e' valido int file() const { return _fileid; } // ritorna il file void set_file(int f); const TString& name() const { return _name; } // ritorna il nome del campo void set_name(const char* n) { _name = n; } void set_from(int f) { if (f > 0) f--; else f = 0; _from = f; } void set_to(int t) { _to = t; } int from() const { return _from; } int to() const { return _to; } int len(TRectype &rec) const; const char* read(const TRelation*) const; const char* read(const TRectype&) const; void write(const char* val, TRelation*) const; void write(const char* val, TRectype& rec) const; }; // Converte una stringa in numero logico o numero tabella int name2log(const char* name); class TRelation_description : public TObject { // gestisce l'uso interattivo di una relazione (scelta campi, descrizione) // domani o doman l'altro gestira' l'editing interattivo e grafico TRelation* _rel; // relation described TString_array _files; TArray _fields; int _cur_file; int _cur_field; bool _menu; TToken_string _cfile, _cfield; protected: void init_files_array(); void read_rel(); public: // TObject virtual bool ok() const { return _files.items() > 0; } public: // "choose" interface: after choosing a field (must return TRUE) // methods allow to know all data concerning the field // parameters set the one initially selected bool choose_file (int id = 0); bool set_cur_file(int id = 0); // file must have been chosen: first file in list if called // before choose_file bool choose_field(const char* fld = ""); // if wanted, build a menu tree and set current field upon // selecting it (should be popup) bool build_menu(const char* title = "Database"); bool remove_menu(); // if choose_file == TRUE or menu has been used these return valid data int file_num(); const char* file_desc(); void file_desc(const char* desc); bool set_field_description(const char* field, const char* des); const char* get_field_description(const char* field); // if choose_field == TRUE or menu has been used these return valid data const char* field_desc(); void field_desc(const char* desc); const char* field_name(); int field_len(); TFieldtypes field_type(); const TString_array& get_all_desc() const { return _files; } void change_relation(TRelation& rel, TString_array& arr); virtual void print_on(ostream& out) const; TRelation_description(TRelation& r); virtual ~TRelation_description(); }; #endif // ** EOF relation.h