git-svn-id: svn://10.65.10.50/branches/R_10_00@23039 c028cbd2-c16b-5b4b-a496-9718f37d4682
		
			
				
	
	
		
			1061 lines
		
	
	
		
			26 KiB
		
	
	
	
		
			C++
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			1061 lines
		
	
	
		
			26 KiB
		
	
	
	
		
			C++
		
	
	
		
			Executable File
		
	
	
	
	
#include <dongle.h>
 | 
						|
#include <prefix.h>
 | 
						|
#include <recarray.h>
 | 
						|
#include <relation.h>
 | 
						|
#include <tabmod.h>
 | 
						|
#include <tabutil.h>
 | 
						|
#include <xvt.h>
 | 
						|
 | 
						|
#include <user.h>
 | 
						|
 | 
						|
#include <time.h>
 | 
						|
 | 
						|
///////////////////////////////////////////////////////////
 | 
						|
// TFast_isamfile
 | 
						|
///////////////////////////////////////////////////////////
 | 
						|
 | 
						|
TFast_isamfile::TFast_isamfile(int logicnum) : TIsamfile(logicnum)
 | 
						|
{
 | 
						|
  int err = open_ex(_excllock, true);
 | 
						|
  if (err != NOERR && err != -60)
 | 
						|
    err = open(_manulock, true);
 | 
						|
  if (err != NOERR)
 | 
						|
    cantread_box(name());
 | 
						|
}
 | 
						|
 | 
						|
TFast_isamfile::~TFast_isamfile() 
 | 
						|
{ 
 | 
						|
  close();
 | 
						|
  // prefix().close_closeable_isamfiles(); // Non strettamente necessario
 | 
						|
}
 | 
						|
 | 
						|
///////////////////////////////////////////////////////////
 | 
						|
// TRecord_Array
 | 
						|
///////////////////////////////////////////////////////////
 | 
						|
 | 
						|
TRecord_array::TRecord_array(const TRectype& r, const char* numfield, int first)
 | 
						|
             : _file(r.num()), _offset(first - 1), _num(numfield)
 | 
						|
{ 
 | 
						|
  read(r); 
 | 
						|
} 
 | 
						|
 | 
						|
TRecord_array::TRecord_array(int logicnum, const char* numfield, int first)
 | 
						|
             : _file(logicnum), _offset(first - 1), _num(numfield)
 | 
						|
{
 | 
						|
  set_key(new TRectype(logicnum));
 | 
						|
} 
 | 
						|
 | 
						|
TRecord_array::TRecord_array(const TRecord_array& a)
 | 
						|
{
 | 
						|
  copy(a);
 | 
						|
}
 | 
						|
 | 
						|
// Questo si che e' il costruttore dei miei sogni
 | 
						|
// Senza cavolate come numfield o necessità di interi record come chiave!
 | 
						|
TRecord_array::TRecord_array(const char* keytok, int logicnum, int first)
 | 
						|
             : _file(logicnum), _offset(first - 1)
 | 
						|
{
 | 
						|
  const RecDes& rd = prefix().get_recdes(logicnum);         // Tracciato record del file
 | 
						|
  const KeyDes& kd = rd.Ky[0];                              // Tracciato della chiave primaria
 | 
						|
  const int nLast = kd.FieldSeq[kd.NkFields-1] % MaxFields; // Posizione dell'ultimo campo della chiave 
 | 
						|
  _num = rd.Fd[nLast].Name;                                 // Nome dell'ultimo campo della chiave 
 | 
						|
 | 
						|
  TRectype* keyrec = new TRectype(logicnum);                // Record chiave (vuoto)
 | 
						|
 | 
						|
  if (keytok && *keytok)                                    // La stringa chiave non e' vuota
 | 
						|
  {
 | 
						|
    const TToken_string key(keytok);
 | 
						|
    TString80 val;                                          // Un campo chiave non puo' mai superare i 70 (4->S0)
 | 
						|
    for (int i = 0; i < kd.NkFields-1; i++)                 // Riempio "quasi" tutta la chiave primaria 
 | 
						|
    {
 | 
						|
      const int nPos = kd.FieldSeq[i] % MaxFields;          // Posizione del campo i della chiave
 | 
						|
      const char* field = rd.Fd[nPos].Name;                 // Nome del campo i della chiave
 | 
						|
      key.get(i, val);                                      // Valore del campo i della chiave
 | 
						|
      keyrec->put(field, val);                              // Riempio il campo chiave corrispondente 
 | 
						|
    }
 | 
						|
  }
 | 
						|
  read(keyrec);                                             // Leggo o inizializzo l'array vuoto
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
TRecord_array::~TRecord_array()
 | 
						|
{
 | 
						|
}
 | 
						|
 | 
						|
void TRecord_array::set_key(TRectype* r)
 | 
						|
{                                                                     
 | 
						|
  CHECK(r != NULL, "TRecord_array can't have a null key");
 | 
						|
  CHECK(r->num() == _file, "Bad key record");
 | 
						|
 | 
						|
  if (_data.objptr(0) != r)
 | 
						|
    _data.add(r, 0);                      // Setta il nuovo campo chiave
 | 
						|
  if (rows() > 0)
 | 
						|
  {
 | 
						|
    const RecDes& recd = r->rec_des();    // Descrizione del record della testata
 | 
						|
    const KeyDes& kd = recd.Ky[0];        // Elenco dei campi della chiave 1
 | 
						|
    
 | 
						|
    // Copia tutti i campi chiave, tranne l'ultimo, in tutti i records
 | 
						|
    TString80 val;
 | 
						|
    for (int i = kd.NkFields-2; i >= 0; i--)
 | 
						|
    {                        
 | 
						|
      const int nf = kd.FieldSeq[i] % MaxFields;
 | 
						|
      const RecFieldDes& rf = recd.Fd[nf];  
 | 
						|
      val = r->get(rf.Name);
 | 
						|
      renum_key(rf.Name, val);
 | 
						|
      CHECKS(i > 0 || val.full(), "First key field can't be empty: ", rf.Name);
 | 
						|
    }
 | 
						|
 | 
						|
    if (_num.find('[') > 0)
 | 
						|
    {
 | 
						|
      const TFieldref field(_num, 0);
 | 
						|
      val = r->get(field.name());
 | 
						|
      val.cut(field.from());
 | 
						|
      renum_key(field.name(), val);
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
const TRectype& TRecord_array::key() const 
 | 
						|
{
 | 
						|
  const TRectype* r = (const TRectype*)_data.objptr(0);
 | 
						|
  CHECK(r != NULL, "TRecord_array lost its key");
 | 
						|
  return *r; 
 | 
						|
}
 | 
						|
 | 
						|
bool TRecord_array::exist(int n) const
 | 
						|
{ 
 | 
						|
  const int i = n > 0 ? n - _offset : -1;
 | 
						|
  TObject* r = _data.objptr(i);
 | 
						|
  return r != NULL;
 | 
						|
}  
 | 
						|
 | 
						|
// @doc EXTERNAL
 | 
						|
 | 
						|
// @mfunc Ritorna la riga <p r>-esima; se tale riga non esiste e se <p create> assume il valore true la riga viene creata
 | 
						|
//
 | 
						|
// @rdesc Ritorna un TRectype alla riga cercata o eventualemete il reference della nuova creata,
 | 
						|
//    NULL nel caso non esista e non venga creata.
 | 
						|
TRectype& TRecord_array::row(
 | 
						|
          int n,  // @parm Numero della riga da creare
 | 
						|
          bool create)  // @parm Indica se creare una nuova riga se non esiste:
 | 
						|
      //
 | 
						|
      // @flag true | Se la riga <p n> non esiste viene creata
 | 
						|
      // @flag false | Se la riga <p n> non esiste non viene creata
 | 
						|
 | 
						|
// @comm Nel caso <p create> sia true e venga richiesta una riga non esistente si crea un nuovo
 | 
						|
//   record copiando la chiave.
 | 
						|
{                    
 | 
						|
  const int i = n >= 0 ? n - _offset : -1;
 | 
						|
  TRectype* r = (TRectype*)_data.objptr(i);
 | 
						|
  if (r == NULL && create)
 | 
						|
  {
 | 
						|
    r = (TRectype*)key().dup();                 // Crea nuovo record copiando la chiave
 | 
						|
    n = _data.add(r, i) + _offset;              // Riassegna n se era negativo!
 | 
						|
    TString16 str; str << n;
 | 
						|
    r->renum_key(_num, str);                    // Aggiorna campo numero riga
 | 
						|
  }
 | 
						|
  CHECKD(r && n > 0, "Bad record number ", n);  
 | 
						|
  return *r;
 | 
						|
}
 | 
						|
 | 
						|
// @doc EXTERNAL
 | 
						|
 | 
						|
// @mfunc Rinumera il campo chiave in seguito a reinsert
 | 
						|
//
 | 
						|
// @rdesc Ritorna se e' stato rinumerato il campo chiave
 | 
						|
bool TRecord_array::renum_key(
 | 
						|
     const char* field,   // @parm Campo della chiave a cui assegnare un nuovo valore
 | 
						|
     const TString& num)  // @parm Nuovo valore da assegnare al campo
 | 
						|
        // @parm long | num | Nuovo valore da assegnare al campo
 | 
						|
 | 
						|
// @syntax bool renum_key(const char* field, const TString& num);
 | 
						|
// @syntax bool renum_key(const char* field, long num);
 | 
						|
 | 
						|
{      
 | 
						|
  CHECKS(_num != field, "You can't renumber field ", field);
 | 
						|
  
 | 
						|
  for (int i = _data.last(); i >= 0; i--)
 | 
						|
  {
 | 
						|
    TRectype* r = (TRectype*)_data.objptr(i);
 | 
						|
    if (r != NULL)
 | 
						|
      r->renum_key(field, num);
 | 
						|
  }  
 | 
						|
  
 | 
						|
  return true; 
 | 
						|
}
 | 
						|
 | 
						|
bool TRecord_array::renum_key(const char* field, long num)
 | 
						|
{   
 | 
						|
  CHECKS(num > 0, "Null key value for field: ", field);
 | 
						|
  TString16 n; n << num;
 | 
						|
  return renum_key(field, n);
 | 
						|
}
 | 
						|
 | 
						|
void TRecord_array::sort(COMPARE_FUNCTION sort_func)
 | 
						|
{
 | 
						|
  TObject * rec =  _data.remove(0);   // salva chiave
 | 
						|
  
 | 
						|
  _data.sort(sort_func);              // ordina
 | 
						|
  _data.insert(rec,0);                // ripristina chiave
 | 
						|
  TString16 n;
 | 
						|
  for (int i = _data.last(); i > 0; i--)
 | 
						|
  {
 | 
						|
    n.cut(0) << i;
 | 
						|
    row(i, false).renum_key(_num, n); // Rinumera
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
int TRecord_array::rec2row(const TRectype& r) const
 | 
						|
{
 | 
						|
  CHECK(r.num() == _file, "Incompatible record");
 | 
						|
  int n = 0;
 | 
						|
  if (_num.find('[') > 0)
 | 
						|
  {
 | 
						|
    const TFieldref fref(_num, 0);
 | 
						|
    n = atoi(fref.read(r));
 | 
						|
  }
 | 
						|
  else
 | 
						|
    n = r.get_int(_num);    // Non e' detto che sia un int!
 | 
						|
  n -= _offset;
 | 
						|
  CHECKD(n >= 0 && n <= 99999, "Bad line number in record ", n + _offset);
 | 
						|
  return n;
 | 
						|
}
 | 
						|
 | 
						|
int TRecord_array::insert_row(TRectype* r)
 | 
						|
{      
 | 
						|
  const int nr = rec2row(*r);
 | 
						|
  CHECK(nr > 0, "You cannot insert a new key");
 | 
						|
  const bool shift = exist(nr);
 | 
						|
  _data.insert(r, nr);
 | 
						|
  if (shift)                     
 | 
						|
  {   
 | 
						|
    TString16 n;
 | 
						|
    for (int f = _data.last(); f > nr; f = _data.pred(f))         
 | 
						|
    {    
 | 
						|
      TRectype& rec = row(f, false);
 | 
						|
      n.cut(0) << long(_offset + f);
 | 
						|
      rec.renum_key(_num, n);
 | 
						|
    }
 | 
						|
  }
 | 
						|
  return nr;
 | 
						|
}
 | 
						|
 | 
						|
int TRecord_array::add_row(TRectype* r)
 | 
						|
{               
 | 
						|
  const int nr = rec2row(*r);
 | 
						|
  if (nr==0)
 | 
						|
    set_key(r);
 | 
						|
  else
 | 
						|
    _data.add(r, nr);
 | 
						|
  return nr;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
// @doc EXTERNAL
 | 
						|
 | 
						|
// @mfunc Elimina le righe vuote
 | 
						|
void TRecord_array::pack()
 | 
						|
{                       
 | 
						|
  _data.pack();
 | 
						|
  
 | 
						|
  TString16 n;
 | 
						|
  for (int i = _data.last(); i > 0; i--)
 | 
						|
  {
 | 
						|
    TRectype* r = (TRectype*)_data.objptr(i);
 | 
						|
    if (r != NULL)
 | 
						|
    {
 | 
						|
      n.cut(0) << long(i+_offset);
 | 
						|
      r->renum_key(_num, n);
 | 
						|
    }  
 | 
						|
  }  
 | 
						|
} 
 | 
						|
 | 
						|
 | 
						|
// @doc EXTERNAL
 | 
						|
 | 
						|
// @mfunc Cancella una riga
 | 
						|
//
 | 
						|
// @rdesc Ritorna se e' riuscito ad eliminare la riga
 | 
						|
bool TRecord_array::destroy_row(
 | 
						|
     int r,   // @parm Numero della riga da eliminare
 | 
						|
     bool pack) // @parm Indica se compattare gli elementi dell'array (default false)
 | 
						|
    // @parm const TRectype& | r | Record da eliminare
 | 
						|
 | 
						|
// @syntax bool destroy_row(int n, bool pack);
 | 
						|
// @syntax bool destroy_row(const TRectype& r, bool pack);
 | 
						|
 | 
						|
{                       
 | 
						|
  CHECKD(r > _offset, "Can't destroy row ", r);
 | 
						|
  const int index = r - _offset;
 | 
						|
  const bool ok = _data.destroy(index, pack);
 | 
						|
  
 | 
						|
  if (ok && pack)
 | 
						|
  {
 | 
						|
    TString16 n;
 | 
						|
    for (int i = _data.size()-1; i >= index; i--)
 | 
						|
    {
 | 
						|
      TRectype* r = (TRectype*)_data.objptr(i);
 | 
						|
      if (r != NULL)
 | 
						|
      {
 | 
						|
        n.cut(0) << long(i+_offset);
 | 
						|
        r->renum_key(_num, n);
 | 
						|
      }  
 | 
						|
    }  
 | 
						|
  }
 | 
						|
  return ok;
 | 
						|
} 
 | 
						|
 | 
						|
void TRecord_array::destroy_rows()
 | 
						|
{                      
 | 
						|
  for (int i = _data.last(); i > 0; i = _data.pred(i))
 | 
						|
    _data.destroy(i);
 | 
						|
} 
 | 
						|
 | 
						|
// @doc EXTERNAL
 | 
						|
 | 
						|
// @mfunc Confronta i campi della chiave 1 scartando l'ultimo
 | 
						|
//
 | 
						|
// @rdesc Ritorna il risulato del confronto:
 | 
						|
//
 | 
						|
// @flag true | Se i campi sono uguali
 | 
						|
// @flag false | Se i campi sono diversi
 | 
						|
bool TRecord_array::good(
 | 
						|
     const TRectype& rec) const  // @parm Record di cui confrontare i campi
 | 
						|
{                                
 | 
						|
  bool yes = key().same_key(rec, 1, 1);
 | 
						|
  if (yes && _num.find('[') > 0)
 | 
						|
  {
 | 
						|
    const TFieldref fref(_num, 0);
 | 
						|
    const char* codtab = fref.name();
 | 
						|
    yes = rec.get(codtab).starts_with(key().get(codtab));
 | 
						|
  }
 | 
						|
  return yes;
 | 
						|
}
 | 
						|
 | 
						|
int TRecord_array::read(TRectype* filter)
 | 
						|
{            
 | 
						|
  CHECK(filter != NULL, "You can't specify NULL as filter ");
 | 
						|
  CHECKD(filter->num() == _file, "Bad key record ", filter->num());
 | 
						|
 | 
						|
#ifndef NDEBUG
 | 
						|
  const TFieldref fref(_num, 0);
 | 
						|
  CHECKS(*fref.read(*filter) < ' ', "You can't specify in the filter the field ", (const char*)_num);
 | 
						|
#endif
 | 
						|
 | 
						|
  _data.destroy();
 | 
						|
  set_key(filter);    
 | 
						|
  int err = NOERR;
 | 
						|
  if (!filter->empty())
 | 
						|
  {
 | 
						|
    TLocalisamfile f(_file);
 | 
						|
    TRectype* rec = (TRectype*)filter->dup();
 | 
						|
    err = rec->read(f, _isgteq);
 | 
						|
    for (int e = err; e == NOERR && good(*rec); e = rec->next(f))
 | 
						|
    {
 | 
						|
      add_row(rec);
 | 
						|
      rec = (TRectype*)(key().dup());
 | 
						|
    }  
 | 
						|
    delete rec;
 | 
						|
  } 
 | 
						|
  else 
 | 
						|
    err = _iskeynotfound;
 | 
						|
  return err;  
 | 
						|
}
 | 
						|
 | 
						|
// @doc EXTERNAL
 | 
						|
 | 
						|
// @mfunc Copia un record array
 | 
						|
//
 | 
						|
// @rdesc Copia il record array passato in quello corrente
 | 
						|
TRecord_array& TRecord_array::copy(
 | 
						|
     const TRecord_array& a) // @parm Record_array da copiare
 | 
						|
{
 | 
						|
  _file = a._file;
 | 
						|
  _data = a._data;
 | 
						|
  _offset = a._offset;
 | 
						|
  _num = a._num;
 | 
						|
  return *this;
 | 
						|
}
 | 
						|
 | 
						|
int TRecord_array::read(const TRectype& filter)
 | 
						|
{ 
 | 
						|
  TRectype* f = (TRectype*)filter.dup();
 | 
						|
  return read(f);  
 | 
						|
}
 | 
						|
 | 
						|
int TRecord_array::remove_from(TIsamfile& f, int pos) const
 | 
						|
{                 
 | 
						|
  int err = NOERR;
 | 
						|
 | 
						|
  TRectype* rec = (TRectype*)key().dup();
 | 
						|
  CHECK(!rec->empty(), "Can't use empty key");
 | 
						|
 | 
						|
  TString16 n; n << pos;
 | 
						|
  rec->renum_key(_num, n);
 | 
						|
 | 
						|
  const TFieldref fref(_num, 0);
 | 
						|
 | 
						|
  for (int e = rec->read(f, _isgteq); e == NOERR && good(*rec); e = rec->next(f))
 | 
						|
  {          
 | 
						|
    const int found = atoi(fref.read(*rec));
 | 
						|
    if (found >= pos)
 | 
						|
    {
 | 
						|
      err = rec->remove(f);             
 | 
						|
      if (err != NOERR)
 | 
						|
        break;
 | 
						|
      pos = found+1;  
 | 
						|
    }
 | 
						|
    else
 | 
						|
      break;    
 | 
						|
  } 
 | 
						|
  delete rec;
 | 
						|
 | 
						|
  return err;
 | 
						|
}
 | 
						|
 | 
						|
int TRecord_array::remove_from(int pos) const
 | 
						|
{
 | 
						|
  TFast_isamfile f(_file);
 | 
						|
  return remove_from(f, pos);
 | 
						|
}
 | 
						|
 | 
						|
int TRecord_array::write(bool re) const
 | 
						|
{          
 | 
						|
  const int EOR = 32000;              // End of records on file
 | 
						|
  int last_on_file = 0;               // Last record found on file
 | 
						|
  int err = NOERR;
 | 
						|
    
 | 
						|
  TFast_isamfile f(_file);
 | 
						|
 | 
						|
  const int u = _data.last();
 | 
						|
 | 
						|
  CHECK(u<1 || !key().empty(), "Can't write rows using an empty key");
 | 
						|
  int i;
 | 
						|
  for (i = 1; i <= u; i++)
 | 
						|
  {
 | 
						|
    const TRectype* r = (TRectype*)_data.objptr(i);
 | 
						|
    
 | 
						|
    if (r != NULL) 
 | 
						|
    {               
 | 
						|
      if (re)
 | 
						|
      {
 | 
						|
        err = r->rewrite(f);     
 | 
						|
        if (err == _iskeynotfound || err == _iseof || err == _isemptyfile)
 | 
						|
          err = r->write(f);           
 | 
						|
        if (err != NOERR) 
 | 
						|
          break;
 | 
						|
      }
 | 
						|
      else
 | 
						|
      {
 | 
						|
        err = r->write(f);     
 | 
						|
        if (err == _isreinsert)
 | 
						|
        {
 | 
						|
          err = r->rewrite(f);           
 | 
						|
          re = true;
 | 
						|
        }  
 | 
						|
        if (err != NOERR) 
 | 
						|
          break;
 | 
						|
      }    
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {                    
 | 
						|
      const int pos = i+_offset;                                       
 | 
						|
      
 | 
						|
      // Se pos < last_on_file sicuramente la read fallira' per cui la riga
 | 
						|
      // non puo' esistere su file: non esistendo nemmeno in memoria non devo
 | 
						|
      // fare assolutamente nulla!
 | 
						|
      if (pos >= last_on_file)                // Puo' esistere su file?
 | 
						|
      {
 | 
						|
        TRectype* rec = (TRectype*)key().dup();
 | 
						|
        CHECK(!rec->empty(), "TRecord_array has an empty key");
 | 
						|
        TString16 n; n << pos;
 | 
						|
        rec->renum_key(_num, n);
 | 
						|
        err = rec->read(f, _isgteq);
 | 
						|
        if (err == NOERR && good(*rec))       // Cerca una riga >= pos sul file
 | 
						|
        {               
 | 
						|
          const TFieldref fref(_num, 0);
 | 
						|
          last_on_file = atoi(fref.read(*rec));
 | 
						|
          if (last_on_file == pos)            // La riga c'era ma ora non piu'
 | 
						|
          {
 | 
						|
            err = (rec->remove(f));           // Cancello il record indesiderato
 | 
						|
            if (err != NOERR) 
 | 
						|
            {         
 | 
						|
              delete rec;                     // Orrore! Un errore di cancellazione!
 | 
						|
              break;
 | 
						|
            }  
 | 
						|
          }
 | 
						|
        } 
 | 
						|
        else
 | 
						|
          last_on_file = EOR;                 // Sul file non ci sono piu' righe da cancellare
 | 
						|
        delete rec;   
 | 
						|
      }  
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  // Cancella eventuali residui successivi
 | 
						|
  if (err == NOERR && last_on_file != EOR)
 | 
						|
    remove_from(f, i + _offset);
 | 
						|
 | 
						|
   return err;
 | 
						|
}
 | 
						|
 | 
						|
int TRecord_array::remove() const
 | 
						|
{
 | 
						|
  return remove_from(1 + _offset);
 | 
						|
}
 | 
						|
 | 
						|
///////////////////////////////////////////////////////////
 | 
						|
// TFile_cache
 | 
						|
///////////////////////////////////////////////////////////  
 | 
						|
 | 
						|
unsigned long TFile_cache::_hits = 0;
 | 
						|
unsigned long TFile_cache::_misses = 0;
 | 
						|
 | 
						|
void TFile_cache::stats(unsigned long& h, unsigned long& m)
 | 
						|
{
 | 
						|
  h = _hits; m = _misses;
 | 
						|
}
 | 
						|
 | 
						|
void TFile_cache::construct(int key)
 | 
						|
{
 | 
						|
  _file = NULL;
 | 
						|
  _key = key;
 | 
						|
  _last_firm = -883;
 | 
						|
  _limit = 0;
 | 
						|
  _test_changes = false;
 | 
						|
  _changed = false;
 | 
						|
}
 | 
						|
 | 
						|
TFile_cache::TFile_cache(TLocalisamfile* f , int key)
 | 
						|
{    
 | 
						|
  construct(key);
 | 
						|
  switch (f->num())
 | 
						|
  {
 | 
						|
  case LF_TABCOM: _filecode << '%' << f->name(); break;
 | 
						|
  case LF_TAB   : _filecode = f->name();         break;
 | 
						|
  case LF_TABMOD: _filecode << '&' << f->name(); break;
 | 
						|
  default       : _filecode << f->num();         break;
 | 
						|
  }
 | 
						|
  init_file(f);
 | 
						|
}
 | 
						|
 | 
						|
TFile_cache::TFile_cache(int num, int key)
 | 
						|
           : _key(key)
 | 
						|
{    
 | 
						|
  construct(key);
 | 
						|
  _filecode << num;
 | 
						|
}
 | 
						|
 | 
						|
TFile_cache::TFile_cache(const char* tab, int key)
 | 
						|
{
 | 
						|
  construct(key);
 | 
						|
  _filecode = tab;
 | 
						|
}
 | 
						|
 | 
						|
TFile_cache::~TFile_cache()
 | 
						|
{ 
 | 
						|
  kill_file();
 | 
						|
}
 | 
						|
 | 
						|
void TFile_cache::kill_file()
 | 
						|
{
 | 
						|
  if (_file != NULL)
 | 
						|
  {
 | 
						|
    delete _file;
 | 
						|
    _file = NULL;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void TFile_cache::init_file(TLocalisamfile* f)
 | 
						|
{
 | 
						|
  if (_file != NULL)
 | 
						|
  {          
 | 
						|
    CHECK(_file != f, "Suspicious file pointer");
 | 
						|
    kill_file();
 | 
						|
  }
 | 
						|
   
 | 
						|
  if (f == NULL)
 | 
						|
  {
 | 
						|
    int logicnum = atoi(_filecode);
 | 
						|
    if (logicnum == 0)
 | 
						|
    {
 | 
						|
      if (_filecode[0] == '&' || _filecode.len() >= 5)
 | 
						|
        _file = new TModule_table(_filecode);
 | 
						|
      else
 | 
						|
        _file = new TTable(_filecode);
 | 
						|
      logicnum = _file->num();
 | 
						|
    }  
 | 
						|
    else
 | 
						|
    {
 | 
						|
      _file = new TLocalisamfile(logicnum);
 | 
						|
    }  
 | 
						|
  } 
 | 
						|
  else
 | 
						|
    _file = f;
 | 
						|
    
 | 
						|
  _last_file_test = _last_file_time = 0;
 | 
						|
  
 | 
						|
  if (_file != NULL)
 | 
						|
  {
 | 
						|
    TDir dir; 
 | 
						|
    dir.get(_file->num(), _nolock, _nordir, _sysdirop);
 | 
						|
    // Se e' un file comune metti a -1, altrimenti alla ditta corrente
 | 
						|
    _last_firm = dir.is_com() ? -1 : prefix().get_codditta();
 | 
						|
    _changed = false;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void TFile_cache::notify_change()
 | 
						|
{
 | 
						|
  _changed = true;
 | 
						|
}
 | 
						|
 | 
						|
void TFile_cache::test_firm()
 | 
						|
{             
 | 
						|
  if (_last_firm < -1)
 | 
						|
    init_file();
 | 
						|
 
 | 
						|
  if (_last_firm >= 0)  // Se e' un file di ditta ...
 | 
						|
  {                     // ... controlla che non sia cambiata
 | 
						|
    const long cur_firm = prefix().get_codditta();
 | 
						|
    if (cur_firm != _last_firm)
 | 
						|
    {                                             
 | 
						|
      _last_firm = cur_firm;
 | 
						|
      destroy();
 | 
						|
    }  
 | 
						|
  }  
 | 
						|
 | 
						|
  if (_test_changes && !_changed && _file != NULL)
 | 
						|
  {
 | 
						|
    // Non controllare troppo spesso la data del file nei programmi batch: aspetta almeno 2 secondi!
 | 
						|
    const unsigned long now = clock();
 | 
						|
    if (now > _last_file_test + 2500)  
 | 
						|
    {
 | 
						|
      TFilename n = prefix().get_filename(_file->num()); n.ext("dbf");
 | 
						|
      FILE_SPEC fs; xvt_fsys_convert_str_to_fspec(n, &fs);
 | 
						|
      const unsigned long ft = xvt_fsys_get_file_attr(&fs, XVT_FILE_ATTR_MTIME);
 | 
						|
      if (ft > _last_file_time && _last_file_time > 0)
 | 
						|
        _changed = true;
 | 
						|
      _last_file_time = ft;
 | 
						|
      _last_file_test = now;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  const bool flush_needed = _test_changes && _changed && _file != NULL;
 | 
						|
 | 
						|
	if (flush_needed)
 | 
						|
  {
 | 
						|
    flush();
 | 
						|
    destroy();
 | 
						|
  }
 | 
						|
  else
 | 
						|
  {
 | 
						|
    if (_limit > 0 && items() > _limit)
 | 
						|
    {
 | 
						|
      const THash_object* rand = _cache.random_hash_object();
 | 
						|
      if (rand != NULL)
 | 
						|
        discard(rand->key());   // Butta via un elemento a caso per fare posto
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void TFile_cache::flush()
 | 
						|
{
 | 
						|
  // Needed by read/write cache
 | 
						|
}
 | 
						|
 | 
						|
bool TFile_cache::already_loaded(const char* code) const
 | 
						|
{
 | 
						|
  return _cache.objptr(code) != NULL;
 | 
						|
}
 | 
						|
 | 
						|
bool TFile_cache::already_loaded(long code) const
 | 
						|
{
 | 
						|
  TString16 str; str << code;
 | 
						|
  return already_loaded(str);
 | 
						|
}
 | 
						|
 | 
						|
const TObject& TFile_cache::query(const char* code)
 | 
						|
{               
 | 
						|
  _error = NOERR;
 | 
						|
  _code = code;
 | 
						|
 | 
						|
  test_firm();
 | 
						|
 | 
						|
  TObject* obj = _cache.objptr(_code);
 | 
						|
 | 
						|
	if (obj == NULL)
 | 
						|
  {     
 | 
						|
    TLocalisamfile& f = file();
 | 
						|
    TRectype& curr = f.curr();
 | 
						|
    if (_code.full())
 | 
						|
    {
 | 
						|
      const RecDes& recd = curr.rec_des();   // Descrizione del record della testata
 | 
						|
      const KeyDes& kd = recd.Ky[_key-1];    // Elenco dei campi della chiave
 | 
						|
      for (int i = 0; i < kd.NkFields; i++)  // Riempie la chiave selezionata
 | 
						|
      {                        
 | 
						|
        const int nf = kd.FieldSeq[i] % MaxFields;
 | 
						|
        const RecFieldDes& rf = recd.Fd[nf];  
 | 
						|
        const char* val = _code.get();
 | 
						|
        if (val && *val)
 | 
						|
          curr.put(rf.Name, val);
 | 
						|
        else  
 | 
						|
          curr.zero(rf.Name);
 | 
						|
      }
 | 
						|
			f.setkey(_key);
 | 
						|
      _error = f.read();
 | 
						|
    } else 
 | 
						|
      _error = _iskeyerr;
 | 
						|
    switch (_error)
 | 
						|
    {
 | 
						|
    case NOERR:
 | 
						|
      break;
 | 
						|
    case _iskeynotfound:
 | 
						|
    case _iseof:
 | 
						|
    case _isemptyfile:
 | 
						|
    default:
 | 
						|
      curr.zero();
 | 
						|
      break;
 | 
						|
    }
 | 
						|
    obj = rec2obj(curr);
 | 
						|
    _cache.add(_code, obj);
 | 
						|
    _misses++;
 | 
						|
  }
 | 
						|
  else
 | 
						|
  {
 | 
						|
    _hits++;
 | 
						|
  }
 | 
						|
  
 | 
						|
  return *obj;
 | 
						|
}
 | 
						|
 | 
						|
bool TFile_cache::discard(const char* victim)
 | 
						|
{
 | 
						|
  if (victim && *victim)
 | 
						|
    return _cache.remove(victim);
 | 
						|
//  destroy(); // ???
 | 
						|
  return true;
 | 
						|
}
 | 
						|
 | 
						|
int TFile_cache::io_result()
 | 
						|
{
 | 
						|
  return _error;
 | 
						|
}
 | 
						|
 | 
						|
long TFile_cache::fill()
 | 
						|
{               
 | 
						|
  test_firm();
 | 
						|
 | 
						|
  TLocalisamfile& f = file();
 | 
						|
 | 
						|
  TRectype& curr = f.curr();
 | 
						|
  const RecDes& recd = curr.rec_des();    // Descrizione del record della testata
 | 
						|
  const KeyDes& kd = recd.Ky[_key-1];    // Elenco dei campi della chiave
 | 
						|
 | 
						|
  f.setkey(_key);
 | 
						|
  for (int err = f.first(); err == NOERR; err = f.next())
 | 
						|
  {        
 | 
						|
    _code.cut(0);
 | 
						|
    for (int i = f.tab() ? 1 :0; i < kd.NkFields; i++)   // Riempie la chiave selezionata
 | 
						|
    {                        
 | 
						|
      const int nf = kd.FieldSeq[i] % MaxFields;
 | 
						|
      const RecFieldDes& rf = recd.Fd[nf];  
 | 
						|
      const char* val = curr.get(rf.Name);
 | 
						|
      _code.add(val);
 | 
						|
    }
 | 
						|
    TObject* obj = rec2obj(curr);
 | 
						|
    _cache.add(_code, obj);
 | 
						|
  }
 | 
						|
  kill_file();  
 | 
						|
 | 
						|
  return _cache.items();
 | 
						|
}
 | 
						|
  
 | 
						|
void TFile_cache::destroy()
 | 
						|
{
 | 
						|
  _cache.destroy();
 | 
						|
  _changed = false;
 | 
						|
  _last_file_test = _last_file_time = 0;
 | 
						|
}
 | 
						|
 | 
						|
TLocalisamfile & TFile_cache::file()
 | 
						|
{
 | 
						|
  if (_file == NULL)
 | 
						|
    init_file();
 | 
						|
  return *_file;
 | 
						|
}
 | 
						|
 | 
						|
///////////////////////////////////////////////////////////
 | 
						|
// TDecoder
 | 
						|
///////////////////////////////////////////////////////////
 | 
						|
 | 
						|
TDecoder::TDecoder(int num, const char* outf, int key)
 | 
						|
        : TFile_cache(num, key), _outf(outf)
 | 
						|
{ }        
 | 
						|
 
 | 
						|
TDecoder::TDecoder(const char* tab, const char* outf, int key)
 | 
						|
        : TFile_cache(tab, key), _outf(outf)
 | 
						|
{ }        
 | 
						|
 | 
						|
TObject* TDecoder::rec2obj(const TRectype& curr) const
 | 
						|
{
 | 
						|
  return new TString(curr.get(_outf));
 | 
						|
}
 | 
						|
 | 
						|
const TString& TDecoder::decode(const char* code)
 | 
						|
{ 
 | 
						|
  if (code && *code)
 | 
						|
  {
 | 
						|
    TString80 key;
 | 
						|
    if (strchr(code, '|') == NULL)
 | 
						|
    {
 | 
						|
      switch (file().num())
 | 
						|
      {
 | 
						|
      case LF_TABMOD: key << file().get("MOD") << '|' << file().get("CUST") << '|'; // Fall down
 | 
						|
      case LF_TAB:
 | 
						|
      case LF_TABCOM: key << file().name() << '|'; break;
 | 
						|
      default: break;
 | 
						|
      }
 | 
						|
    }
 | 
						|
    key << code;
 | 
						|
    const TString& obj = (const TString&)query(key);
 | 
						|
    return obj;
 | 
						|
  }
 | 
						|
  return EMPTY_STRING;
 | 
						|
}
 | 
						|
 | 
						|
const TString& TDecoder::decode(long code)
 | 
						|
{ 
 | 
						|
  if (code > 0)
 | 
						|
  {
 | 
						|
    TString8 c; c << code;
 | 
						|
    return decode(c);
 | 
						|
  }
 | 
						|
  return EMPTY_STRING;
 | 
						|
}  
 | 
						|
 | 
						|
///////////////////////////////////////////////////////////
 | 
						|
// TRecord_cache
 | 
						|
///////////////////////////////////////////////////////////
 | 
						|
 | 
						|
TRecord_cache::TRecord_cache(TLocalisamfile *f, int key)
 | 
						|
             : TFile_cache(f, key)
 | 
						|
{ }        
 | 
						|
 | 
						|
TRecord_cache::TRecord_cache(int num, int key)
 | 
						|
             : TFile_cache(num, key)
 | 
						|
{ }        
 | 
						|
 
 | 
						|
TRecord_cache::TRecord_cache(const char* tab, int key)
 | 
						|
             : TFile_cache(tab, key)
 | 
						|
{ }        
 | 
						|
 | 
						|
TObject* TRecord_cache::rec2obj(const TRectype& curr) const
 | 
						|
{
 | 
						|
  return new TRectype(curr);
 | 
						|
}
 | 
						|
 | 
						|
const TRectype& TRecord_cache::get(const char* key)
 | 
						|
{
 | 
						|
  const TRectype& rec = (const TRectype&)query(key);
 | 
						|
  return rec;
 | 
						|
}  
 | 
						|
 | 
						|
const TRectype& TRecord_cache::get(long key)
 | 
						|
{
 | 
						|
  TString8 str; str << key;
 | 
						|
  return get(str);
 | 
						|
}
 | 
						|
 | 
						|
const TString& TRecord_cache::get(const char* key, const char* field)
 | 
						|
{ return get(key).get(field); }  
 | 
						|
 | 
						|
const TString& TRecord_cache::get(long key, const char* field)
 | 
						|
{ return get(key).get(field); }  
 | 
						|
 | 
						|
///////////////////////////////////////////////////////////
 | 
						|
// TDB_cache
 | 
						|
///////////////////////////////////////////////////////////
 | 
						|
 | 
						|
TRecord_cache& TDB_cache::rec_cache(int file)
 | 
						|
{
 | 
						|
  CHECKD(file >= LF_USER && file < LF_EXTERNAL, "Invalid file ", file);
 | 
						|
  TRecord_cache* rc = (TRecord_cache*)objptr(file);
 | 
						|
  if (rc == NULL)
 | 
						|
  {
 | 
						|
    rc = new TRecord_cache(file);
 | 
						|
    const int reclen = prefix().get_reclen(file);
 | 
						|
    int limit = (1024*1024)/reclen; // Al massimo dedico un mega ad ogni cache
 | 
						|
    if (limit > 1024) limit = 1024;  
 | 
						|
    rc->set_items_limit(limit);
 | 
						|
    rc->test_file_changes();
 | 
						|
    add(rc, file);
 | 
						|
  }
 | 
						|
  return *rc;
 | 
						|
}
 | 
						|
 | 
						|
int TDB_cache::build_table_key(const char* table, const char* key, TToken_string& k) const
 | 
						|
{
 | 
						|
  CHECK(table && *table, "Invalid Table code");
 | 
						|
  TString16 tablename = table; // Attenzione posso avere tabelle di modulo come &PC000883BAR
 | 
						|
  int file = LF_TAB;
 | 
						|
  k.cut(0);
 | 
						|
  if (!isalnum(*table)) // gestisco i casi come %IVA e &AUT
 | 
						|
  {
 | 
						|
    tablename.ltrim(1); // toglie carattere speciale 
 | 
						|
    switch (*table)
 | 
						|
    {
 | 
						|
    case '%': file = LF_TABCOM; break;
 | 
						|
    case '^': file = LF_TABGEN; break;
 | 
						|
    case '&': file = LF_TABMOD;
 | 
						|
      {
 | 
						|
        const TModule_table mt(table);
 | 
						|
        k = mt.module();
 | 
						|
        k.add(mt.customer());
 | 
						|
        tablename = mt.name();
 | 
						|
      }
 | 
						|
      break;
 | 
						|
    case '$':
 | 
						|
    default : file = LF_TAB; break;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  k.add(tablename);
 | 
						|
  k.add(key);
 | 
						|
  return file;
 | 
						|
}
 | 
						|
 | 
						|
const TRectype& TDB_cache::get(const char* table, const char* key)
 | 
						|
{
 | 
						|
  TToken_string tabkey;
 | 
						|
  const int file = build_table_key(table, key, tabkey);
 | 
						|
  return get(file, tabkey);
 | 
						|
}
 | 
						|
 | 
						|
const TRectype& TDB_cache::get(const TRectype& curr)
 | 
						|
{
 | 
						|
  const int num = curr.num();             // Numero logico del file (o tabella)
 | 
						|
  const RecDes& recd = curr.rec_des();    // Descrizione del record della testata
 | 
						|
  const KeyDes& kd = recd.Ky[0];          // Elenco dei campi della chiave 1
 | 
						|
  TToken_string key;
 | 
						|
  for (int i = 0; i < kd.NkFields; i++)   // Riempie la chiave
 | 
						|
  {                        
 | 
						|
    const int nf = kd.FieldSeq[i] % MaxFields;
 | 
						|
    const RecFieldDes& rf = recd.Fd[nf];  
 | 
						|
    key.add(curr.get(rf.Name));
 | 
						|
  }
 | 
						|
  return get(num, key);
 | 
						|
}
 | 
						|
 | 
						|
const TString& TDB_cache::get(int file, const char* key_tok, const char * campo) 
 | 
						|
{ return get(file, key_tok).get(campo); }
 | 
						|
 | 
						|
const TString& TDB_cache::get(int file, long key, const char * campo) 
 | 
						|
{ return get(file, key).get(campo); }
 | 
						|
 | 
						|
const TString& TDB_cache::get(const char* table, const char* key_tok, const char * campo) 
 | 
						|
{ return get(table, key_tok).get(campo); }
 | 
						|
 | 
						|
const TRectype& TDB_cache::get_rec(int file, const char* key1, const char* key2, const char* key3, const char* key4)
 | 
						|
{
 | 
						|
  TToken_string tok;
 | 
						|
  if (key1 && *key1)
 | 
						|
    tok= key1;
 | 
						|
  if (key2) 
 | 
						|
  {
 | 
						|
    tok.add(key2,1);
 | 
						|
    if (key3) 
 | 
						|
    {
 | 
						|
      tok.add(key3);
 | 
						|
      if (key4) 
 | 
						|
        tok.add(key4);
 | 
						|
    }
 | 
						|
  }
 | 
						|
  return get(file, tok);
 | 
						|
}
 | 
						|
 | 
						|
bool TDB_cache::discard(int file, const char* key)
 | 
						|
{ 
 | 
						|
  return rec_cache(file).discard(key); 
 | 
						|
}
 | 
						|
 | 
						|
bool TDB_cache::discard(const char *table, const char* key)
 | 
						|
{
 | 
						|
  TToken_string tabkey;
 | 
						|
  const int file = build_table_key(table, key, tabkey);
 | 
						|
  return rec_cache(file).discard(tabkey); 
 | 
						|
}
 | 
						|
 | 
						|
bool TDB_cache::discard(const TRectype& curr)
 | 
						|
{
 | 
						|
  const int file = curr.num();            // Numero logico del file (o tabella)
 | 
						|
  const RecDes& recd = curr.rec_des();    // Descrizione del record della testata
 | 
						|
  const KeyDes& kd = recd.Ky[0];          // Elenco dei campi della chiave 1
 | 
						|
 | 
						|
  TToken_string code;
 | 
						|
  for (int i = 0; i < kd.NkFields; i++)   // Riempie la chiave
 | 
						|
  {                        
 | 
						|
    const int nf = kd.FieldSeq[i] % MaxFields;
 | 
						|
    const RecFieldDes& rf = recd.Fd[nf];  
 | 
						|
    const char* val = curr.get(rf.Name);
 | 
						|
    code.add(val);
 | 
						|
  }
 | 
						|
  return discard(file, code);
 | 
						|
}
 | 
						|
 | 
						|
TDB_cache& cache()
 | 
						|
{
 | 
						|
  static TDB_cache* _frate_cercone = NULL;
 | 
						|
  if (_frate_cercone == NULL)
 | 
						|
    _frate_cercone = new TDB_cache;
 | 
						|
  return *_frate_cercone;
 | 
						|
}
 | 
						|
 | 
						|
TRecord_cache& rec_cache(int file) 
 | 
						|
{ return cache().rec_cache(file); }
 | 
						|
 | 
						|
// Utente corrente (elemento 0) e suoi gruppi di appartenenza (dall'elemento 1 in poi)
 | 
						|
const TString_array& user_and_groups()
 | 
						|
{
 | 
						|
  static TString_array _uag;
 | 
						|
 
 | 
						|
  TString80 u = user();
 | 
						|
  if (u.blank())
 | 
						|
    u = dongle().administrator();
 | 
						|
 | 
						|
  if (_uag.empty() || u != _uag.row(0).get(0))
 | 
						|
  {
 | 
						|
    _uag.destroy();
 | 
						|
    if (prefix_valid()) // Costruisce lista dei gruppi solo se possibile
 | 
						|
    {
 | 
						|
      TFast_isamfile user(LF_USER);
 | 
						|
      while (u.full())
 | 
						|
      {
 | 
						|
        TToken_string* usr = new TToken_string(u);
 | 
						|
        _uag.add(usr);
 | 
						|
        user.put(USR_USERNAME, u);
 | 
						|
        if (user.read() == NOERR)
 | 
						|
        {
 | 
						|
          if (user.curr().exist(USR_PERMISSION))
 | 
						|
            usr->add(user.get(USR_PERMISSION));
 | 
						|
          u = user.get(USR_GROUPNAME);
 | 
						|
        }
 | 
						|
        else
 | 
						|
          break;
 | 
						|
      }
 | 
						|
    }
 | 
						|
    else
 | 
						|
      _uag.add(u);
 | 
						|
  }
 | 
						|
  return _uag;
 | 
						|
}
 | 
						|
 | 
						|
void TDB_cache::discard(int file)
 | 
						|
{
 | 
						|
  TRecord_cache* rc = (TRecord_cache*)objptr(file);
 | 
						|
  if (rc != NULL)  
 | 
						|
  {
 | 
						|
    rc->flush();
 | 
						|
	  rc->destroy();
 | 
						|
  }
 | 
						|
}
 |