campo-sirio/include/recarray.cpp
guy e9a34d143f Migliorata gestione sheet ricerca
git-svn-id: svn://10.65.10.50/branches/R_10_00@23179 c028cbd2-c16b-5b4b-a496-9718f37d4682
2016-02-25 11:34:01 +00:00

1067 lines
27 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);
}
const TRectype& TDB_cache::get_rec(int file, char c, long n)
{
char key[16]; sprintf_s(key, sizeof(key), "%c|%ld", c, n);
return get(file, key);
}
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();
}
}