From 69c8f45fdeb102e5ec4767249beff5a85a59e098 Mon Sep 17 00:00:00 2001 From: guy Date: Thu, 4 Mar 2004 13:40:28 +0000 Subject: [PATCH] Patch level : 2.1 nopatch Files correlati : ba0.exe ba8.exe Ricompilazione Demo : [ ] Commento : Aggiunta fantastica applicazione per la generazione di query SQL git-svn-id: svn://10.65.10.50/trunk@11804 c028cbd2-c16b-5b4b-a496-9718f37d4682 --- ba/ba0100.cpp | 17 +- ba/ba0101.cpp | 2 +- ba/ba8.cpp | 6 +- ba/ba8.h | 3 + ba/ba8100.cpp | 5 +- ba/ba8200.cpp | 1182 ++++++++++++++++++++++++++++++++++++++++++++++++ ba/ba8200.h | 19 + ba/ba8200a.uml | 215 +++++++++ ba/ba8200b.h | 0 ba/ba8200b.uml | 95 ++++ ba/ba8201.cpp | 692 ++++++++++++++++++++++++++++ ba/ba8201.h | 70 +++ 12 files changed, 2297 insertions(+), 9 deletions(-) create mode 100755 ba/ba8200.cpp create mode 100755 ba/ba8200.h create mode 100755 ba/ba8200a.uml create mode 100755 ba/ba8200b.h create mode 100755 ba/ba8200b.uml create mode 100755 ba/ba8201.cpp create mode 100755 ba/ba8201.h diff --git a/ba/ba0100.cpp b/ba/ba0100.cpp index 7e5e29c48..017120a2a 100755 --- a/ba/ba0100.cpp +++ b/ba/ba0100.cpp @@ -850,7 +850,6 @@ bool TMenu_application::test_programs() test = prawin.get_bool("TestPrograms"); } - TExternal_app app("ba1 -6"); while (test) { TConfig install("install.ini", "Main"); @@ -871,7 +870,7 @@ bool TMenu_application::test_programs() { if (dongle().active(module)) { - const TString16 code = scan.token().left(2); + const TString4 code = scan.token().left(2); TToken_string* mytok = (TToken_string*)my_modules.objptr(code); TToken_string* histok = (TToken_string*)his_modules.objptr(code); @@ -895,7 +894,12 @@ bool TMenu_application::test_programs() if (dangerous.empty() || (!more && !yesno_box(TR("Si desidera aggiornare i moduli adesso?")))) break; + + const TString16 old_user = user(); + user() = dongle().administrator(); // Divento temporaneamente amministratore + TExternal_app app("ba1 -6"); app.run(); + user() = old_user; // Ripristino utente normale more = TRUE; // ricontrolla } _menu.set_dangerous_modules(dangerous); @@ -1123,6 +1127,13 @@ bool TMenu_application::choose_editors() return TRUE; } +HIDDEN int dir_sort(const TObject** d1, const TObject** d2) +{ + const TString& s1 = (const TString&)**d1; + const TString& s2 = (const TString&)**d2; + return stricmp(s1, s2); +} + HIDDEN bool study_handler(TMask_field& f, KEY k) { bool ok = TRUE; @@ -1160,7 +1171,7 @@ HIDDEN bool study_handler(TMask_field& f, KEY k) } xvt_slist_destroy(dirs); - sht.rows_array().sort(); + sht.rows_array().TArray::sort(dir_sort); if (sht.run() == K_ENTER) f.set(sht.row(-1)); // -1 = selected row } diff --git a/ba/ba0101.cpp b/ba/ba0101.cpp index f2618b318..9b3acfc8e 100755 --- a/ba/ba0101.cpp +++ b/ba/ba0101.cpp @@ -257,7 +257,7 @@ bool TMenuitem::perform_program() const TExternal_app a(_action); a.run(FALSE,3); - const bool maintenance_app = _action.compare("ba1 -0", 6, TRUE) == 0; + const bool maintenance_app = _action.starts_with("ba1 -0", TRUE) == 0; if (maintenance_app) { char line1[16],line2[16]; diff --git a/ba/ba8.cpp b/ba/ba8.cpp index c7a4a9664..ac8c5efad 100755 --- a/ba/ba8.cpp +++ b/ba/ba8.cpp @@ -7,10 +7,8 @@ int main(int argc, char** argv) const int n = argc > 1 ? atoi(argv[1]+1) : 0; switch (n) { - case 0: - default: - ba8100(argc, argv); - break; + case 1: ba8200(argc, argv); break; + default: ba8100(argc, argv); break; } return 0; } \ No newline at end of file diff --git a/ba/ba8.h b/ba/ba8.h index e5b637699..ae7950f19 100755 --- a/ba/ba8.h +++ b/ba/ba8.h @@ -1 +1,4 @@ int ba8100(int argc, char* argv[]); +int ba8200(int argc, char* argv[]); + + diff --git a/ba/ba8100.cpp b/ba/ba8100.cpp index 7d0b5936d..3b2c6fad1 100755 --- a/ba/ba8100.cpp +++ b/ba/ba8100.cpp @@ -327,7 +327,10 @@ void TSelector_app::main_loop() } TMask m(mask_name); - xvt_vobj_minimize(TASK_WIN); + + TTemp_window tw(TASK_WIN); + tw.minimize(); + for (int f = m.fields()-1; f >= 0; f--) { TMask_field& field = m.fld(f); diff --git a/ba/ba8200.cpp b/ba/ba8200.cpp new file mode 100755 index 000000000..bdb70118b --- /dev/null +++ b/ba/ba8200.cpp @@ -0,0 +1,1182 @@ +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../../sqlite/sources/sqlite.h" + +#include "ba8200.h" +#include "ba8201.h" + +/////////////////////////////////////////////////////////// +// Utility +/////////////////////////////////////////////////////////// + +// Ritorna l'indice di somiglianza tra due stringhe: +// va da 0.0 (assolutamente diverse) a 1.0 (veramente identiche) +// Inventata sul momento: in letteratura si dovrebbe trovare di meglio +double fuzzy_strcmp(const char* s1, const char* s2) +{ + if (xvt_str_compare_ignoring_case(s1, s2) == 0) + return 1.0; + + TString p1(s1), p2(s2); + if (p1.len() > p2.len()) + { p1 = s2; p2 = s1; } + p1.upper(); p2.upper(); + const int l1 = p1.len(), l2 = p2.len(); + + double idx = 0.0; + if (p2.find(p1) < 0) + { + if (l1 >= 3) + { + for (int i = l1-3; i >= 0; i--) + if (p2.find(p1.mid(i, 3), i) >= i) + idx++; + idx /= l1-2; + } + } + else + idx = 1.0; + + if (idx >= 1.0) + { + idx = (double)l1 / (double)l2; + if (idx >= 1.0) + idx = 0.99; + } + + return idx; +} + +/////////////////////////////////////////////////////////// +// TRelation_node & TRelation_tree +/////////////////////////////////////////////////////////// + +// Informazioni relative ad un nodo della relazione: +class TRelation_node : public TObject +{ + const TRelation_node* _father; // Puntatore all'eventuale padre + int _logicnum; // Numero logico + TString16 _name, _alias; // Nome ed Alias + TString_array _join_fields; // Elenco dei campi di join + +public: + bool description(TString& str) const; + int num() const { return _logicnum; } + const TString& name() const { return _name; } + const TString& alias() const { return _alias; } + const TString& id() const { return _alias.not_empty() ? _alias : _name; } + const TRelation_node* father() const { return _father; } + + void set_num(int num); + void set_alias(const char* alias) { _alias = alias; } + + TString_array& join() { return _join_fields; } + + TRelation_node(const TRelation_node* father, int ln, const char* alias); + virtual ~TRelation_node() {} +}; + +bool TRelation_node::description(TString& str) const +{ + const bool ok = _logicnum >= LF_USER; + if (ok) + { + const FileDes& fd = prefix().get_filedes(_logicnum); + str = _name; + if (_alias.not_empty()) + str << " ALIAS " << _alias; + str << " (" << fd.Des << ')'; + } + return ok; +} + +void TRelation_node::set_num(int num) +{ + _logicnum = num; + const FileDes& fd = prefix().get_filedes(_logicnum); + const TFilename name = fd.SysName; + _name = name.name(); _name.upper(); +} + +TRelation_node::TRelation_node(const TRelation_node* father, int ln, const char* alias) + : _father(father) +{ + set_num(ln); + set_alias(alias); +} + +// Albero di una relazione +class TRelation_tree : public TObject_tree +{ +protected: + virtual bool get_description(TString& str) const; + +public: + bool find_id(const TString& id); +}; + +bool TRelation_tree::get_description(TString& str) const +{ + const TRelation_node* rn = (const TRelation_node*)curr_node(); + if (rn != NULL) + return rn->description(str); + return false; +} + +static bool find_id_callback(TTree& tree, void* jolly, word flags) +{ + const TRelation_node* n = (const TRelation_node*)tree.curr_node(); + if (n != NULL) + { + const TString& id = *(const TString*)jolly; + return n->id() == id; + } + return false; +} + +bool TRelation_tree::find_id(const TString& id) +{ + bool ok = goto_root(); + if (ok) + { + scan_depth_first(find_id_callback, (void*)&id); + const TRelation_node* n = (const TRelation_node*)curr_node(); + ok = n != NULL && n->id() == id; + } + return ok; +} + +/////////////////////////////////////////////////////////// +// TTable_mask +/////////////////////////////////////////////////////////// + +// Maschera per inserire/editare un nodo dell'albero +class TTable_mask : public TAutomask +{ +protected: + virtual bool on_field_event(TOperable_field& o, TField_event e, long jolly); + +protected: + int name2num(const TString& name) const; + int father_logicnum() const; + const char* find_linked_field(int logicnum, const RecFieldDes& fd) const; + +public: + int son_logicnum() const; + void mask2node(TRelation_node& node); + + TTable_mask(); + ~TTable_mask(); +}; + +// Converte un nome di tabella nel suo numero loigico +int TTable_mask::name2num(const TString& name) const +{ + int num = 0; + if (name.not_empty()) + { + TList_sheet& sht = *efield(F_SON).sheet(); + TString_array& arr = sht.rows_array(); + FOR_EACH_ARRAY_ROW(arr, i, row) + { + if (name == row->get(0)) + { + num = row->get_int(1); + break; + } + } + } + return num; +} + +int TTable_mask::father_logicnum() const +{ + return name2num(get(F_FATHER)); +} + +int TTable_mask::son_logicnum() const +{ + return name2num(get(F_SON)); +} + +// Dato il numero logico di una tabella ed un campo (di un'altra tabella) +// cerca il campo piu' simile all'interno del tracciato record +const char* TTable_mask::find_linked_field(int logicnum, const RecFieldDes& fd) const +{ + const RecDes& rd = prefix().get_recdes(logicnum); + int nBest = -1; + double dBest = 0.0; + for (int i = 0; i < rd.NFields; i++) + { + const RecFieldDes& field = rd.Fd[i]; + if (field.TypeF == fd.TypeF && field.Len == fd.Len) + { + const double fuzzy = fuzzy_strcmp(field.Name, fd.Name); + if (fuzzy > dBest) + { + nBest = i; + dBest = fuzzy; + if (dBest == 1.0) + break; + } + } + } + return nBest >= 0 ? rd.Fd[nBest].Name : ""; +} + +bool TTable_mask::on_field_event(TOperable_field& o, TField_event e, long jolly) +{ + switch (o.dlg()) + { + case F_SON: + if (e == fe_modify) + { + const int logicnum = son_logicnum(); + const int fathernum = father_logicnum(); + + TSheet_field& sheet = sfield(F_SHEET); + sheet.destroy(); + if (logicnum >= LF_USER && fathernum >= LF_USER) + { + const RecDes& rd = prefix().get_recdes(logicnum); + const KeyDes& kd = rd.Ky[0]; + TToken_string tok; + for (int j = 0; j < kd.NkFields; j++) + { + const int n = kd.FieldSeq[j] % MaxFields; + tok = rd.Fd[n].Name; + tok.add(find_linked_field(fathernum, rd.Fd[n])); + sheet.rows_array().add(tok); + } + } + sheet.force_update(); + } + break; + case F_SHEET: + if (e == se_query_add || e == se_query_del) + return false; + break; + default: + break; + } + + return true; +} + +// Trasferisce le informazioni dalla maschera ad un nodo della tabella +void TTable_mask::mask2node(TRelation_node& son) +{ + son.set_num(son_logicnum()); + son.set_alias(get(F_SON_ALIAS)); + + if (son.father() != NULL) + { + TString_array& join = son.join(); + join.destroy(); + + TSheet_field& sheet = sfield(F_SHEET); + FOR_EACH_SHEET_ROW(sheet, i, row) + { + TToken_string* newrow = new TToken_string(50, '='); + newrow->add(row->get(0)); + newrow->add(row->get()); + join.add(newrow); + } + } +} + +TTable_mask::TTable_mask() : TAutomask("ba8200b") +{ + FileDes dir; + CGetFile(LF_DIR, &dir, _nolock, NORDIR); + const int nfiles = (int)dir.EOD; + + TList_sheet& sht = *efield(F_SON).sheet(); + TToken_string tt(80); + TFilename n; + for (int logic = LF_USER; logic < nfiles; logic++) + { + const FileDes& fd = prefix().get_filedes(logic); + n = fd.SysName; n = n.name(); n.upper(); + tt = n; + tt.add(logic); + tt.add(fd.Des); + sht.rows_array().add(tt); + } + sht.rows_array().sort(); +} + +TTable_mask::~TTable_mask() +{ } + +/////////////////////////////////////////////////////////// +// TQuery_mask +/////////////////////////////////////////////////////////// + +// Maschera principale della costruzione della query +class TQuery_mask : public TAutomask +{ + TRelation_tree _tree; // Albero della relazione + int _curr_num; // Numero del file corrente + bool _dragster; // Operazione di trascinamento in corso + + TFilename _curr_query;// Nome completo della query in editazione + bool _is_dirty; // Maschera cambiata dall'ultimo caricamento + +protected: + virtual void handler(WINDOW win, EVENT* ep); + virtual bool on_field_event(TOperable_field& o, TField_event e, long jolly); + +protected: + bool add_file_to_tree(); + void add_field_to_sheet(bool update = true); + void add_asterisk_to_sheet(); + bool edit_file_in_tree(); + + static int sort_fields(const TObject** r1, const TObject** r2); + void fill_fields(); + void enable_sql_button(); + void enable_field_buttons(); + + void tree2sql(TString& str); + void sheet2sql(); + void edit_query(); + + bool select_query(); + bool load_tables_tree(TXmlItem& tables); + bool load_fields_sheet(TXmlItem& fields); + void global_reset(); + + bool save_tables_tree(TXmlItem& xml); + bool save_fields_sheet(TXmlItem& xml); + void export(const char* ext); + +public: + bool load_query(); + bool save_query(); + bool save_if_needed(); + bool delete_query(); + + void get_sql_path(TFilename& path) const; + bool get_qry_path(TFilename& path) const; + + TRelation_node* curr_node(); + TQuery_mask(); +}; + +// Nodo corrente dell'albero +TRelation_node* TQuery_mask::curr_node() +{ + TTree_field& tf = tfield(F_TABLES); + tf.goto_selected(); + TRelation_node* n = (TRelation_node*)_tree.curr_node(); + return n; +} + +// Aggiunge interattivamente un nodo all'albero +bool TQuery_mask::add_file_to_tree() +{ + TTable_mask msk; + + const TRelation_node* father = curr_node(); + if (father != NULL) + { + msk.set(F_FATHER, father->name()); + msk.set(F_FATHER_ALIAS, father->alias()); + } + msk.disable(DLG_DELREC); + + bool ok = msk.run() == K_ENTER; + if (ok) + { + TRelation_node* son = new TRelation_node(father, msk.son_logicnum(), msk.get(F_SON_ALIAS)); + msk.mask2node(*son); + _tree.add_son(son); + _tree.expand_all(); + tfield(F_TABLES).win().force_update(); + + _curr_num = son->num(); + fill_fields(); + } + + return ok; +} + +// Modifica interattivamente il nodo corrente dell'albero +bool TQuery_mask::edit_file_in_tree() +{ + TRelation_node* son = curr_node(); + if (son == NULL) + return false; + + const TRelation_node* father = NULL; + if (_tree.goto_father()) + father = (const TRelation_node*)_tree.curr_node(); + + TTable_mask msk; + if (father != NULL) + { + msk.set(F_FATHER, father->name()); + msk.set(F_FATHER_ALIAS, father->alias()); + + TString_array& arr = son->join(); + FOR_EACH_ARRAY_ROW(arr, i, row) + { + TToken_string& newrow = msk.sfield(F_SHEET).row(-1); + FOR_EACH_TOKEN((*row), tok) + { + TToken_string str(tok, '.'); + newrow.add(str.get(1)); + } + } + } + msk.set(F_SON, son->name()); + msk.set(F_SON_ALIAS, son->alias()); + + bool update = true; + switch (msk.run()) + { + case K_ENTER: + msk.mask2node(*son); + break; + case K_DEL: + if (curr_node() != NULL) + { + _tree.kill_node(); + _tree.goto_root(); + } + break; + default: + update = false; + break; + } + + if (update) + { + TTree_field& tf = tfield(F_TABLES); + tf.win().force_update(); + + _curr_num = 0; + if (tf.select_current()) + { + const TRelation_node* curr = curr_node(); + if (curr != NULL) + _curr_num = curr->num(); + } + fill_fields(); + } + + return ok; +} + +// Ordina per importanza i campi di un tracciato record +int TQuery_mask::sort_fields(const TObject** r1, const TObject** r2) +{ + TToken_string& s1 = (TToken_string&)**r1; + TToken_string& s2 = (TToken_string&)**r2; + const int w1 = s1.get_int(4); + const int w2 = s2.get_int(4); + int cmp = w2-w1; + if (cmp == 0) + cmp = strcmp(s1.get(0), s2.get(0)); + return cmp; +} + +// Riempie la lista dei campi del file corrente +void TQuery_mask::fill_fields() +{ + TSheet_field& sht = sfield(F_FIELDS); + sht.destroy(); + if (_curr_num >= LF_USER) + { + TRelation rel(_curr_num); + TRelation_description reldes(rel); + + const RecDes& rd = prefix().get_recdes(_curr_num); + for (int i = 0; i < rd.NFields; i++) + { + TToken_string& row = sht.row(-1); + const RecFieldDes& fd = rd.Fd[i]; + row = fd.Name; + row.add(fd.Len); + + int weight = 0; + for (int k = 0; k < rd.NKeys; k++) + { + const KeyDes& kd = rd.Ky[k]; + for (int j = 0; j < kd.NkFields; j++) + { + const int n = kd.FieldSeq[j] % MaxFields; + if (n == i) + { + if (weight == 0) + { + weight = (rd.NKeys-k)*100 + kd.NkFields-j; + row.add(k+1); + } + else + row << ' ' << (k+1); + } + } + } + row.add(reldes.get_field_description(fd.Name), 3); + row.add(weight, 4); + } + sht.rows_array().TArray::sort(sort_fields); + } + sht.force_update(); + enable_field_buttons(); +} + +// Aggiunge il campo selezionato nello spredasheet F_FIELDS allo spreadsheet F_SHEET +void TQuery_mask::add_field_to_sheet(bool update) +{ + const TRelation_node* n = curr_node(); + if (n != NULL) + { + TSheet_field& sf = sfield(F_FIELDS); + const int r = sf.selected(); + if (r >= 0) + { + TToken_string& rowsel = sf.row(r); + TSheet_field& ff = sfield(F_SHEET); + TToken_string& row = ff.row(-1); + row = n->id(); + row.add(rowsel.get(0)); + _is_dirty = true; + if (update) + { + ff.force_update(); + enable_sql_button(); + } + } + } +} + +// Aggiunge tutte le colonne allo sheet +void TQuery_mask::add_asterisk_to_sheet() +{ + const TRelation_node* n = curr_node(); + if (n != NULL) + { + TSheet_field& sf = sfield(F_FIELDS); + const int nLast = sf.items()-1; + for (int i = 0; i <= nLast; i++) + { + sf.select(i); + add_field_to_sheet(i == nLast); + } + sf.select(0); + } +} + +// Decide se attivare o meno il bottone SQL +void TQuery_mask::enable_sql_button() +{ + const TSheet_field& s = sfield(F_SHEET); + enable(F_GENSQL, s.items() > 0); +} + +// Decide se attivare o meno i bottoni di aggiunta campi +void TQuery_mask::enable_field_buttons() +{ + const TSheet_field& sf = sfield(F_FIELDS); + const bool ok = sf.items() > 0; + enable(DLG_USER, ok); + enable(F_ASTERISK, ok); +} + +static bool sql_tree_handler(TTree& tree, void* jolly, word flags) +{ + TRelation_node* rn = (TRelation_node*)tree.curr_node(); + TString& sql = *(TString*)jolly; + TString_array& join = rn->join(); + TString str; + FOR_EACH_ARRAY_ROW(join, i, row) + { + if (sql.find(*row) < 0) + { + if (sql.empty()) + sql << "WHERE "; + else + sql << "AND"; + sql << '(' << rn->id() << '.' << row->get(0) << '='; + str = row->get(); + if (isalpha(str[0])) + sql << rn->father()->id() << '.'; + sql << str << ')'; + } + } + return false; // Don't stop search +} + +// Riempie una stringa SQL con la relazione tra le tabelle +void TQuery_mask::tree2sql(TString& str) +{ + if (_tree.goto_root()) + _tree.scan_depth_first(sql_tree_handler, &str); +} + +// Genera una query SQL a partire dallo spreadsheet F_SHEET +void TQuery_mask::sheet2sql() +{ + const TSheet_field& sheet = sfield(F_SHEET); + TAssoc_array tables; + TToken_string select(50, ','), from(50, ','); + + FOR_EACH_SHEET_ROW(sheet, i, row) + { + const TString8 table = row->get(0); + const TString16 field = row->get(); + if (!table.blank() && !field.blank()) + { + if (!tables.is_key(table)) + { + tables.add(table); + from.add(table); + } + select.add(table); + select << '.' << field; + } + } + if (tables.items() == 1) + { + select.cut(0); + FOR_EACH_SHEET_ROW(sheet, i, row) + select.add(row->get(1)); + } + + TString sql; + sql << "SELECT " << select << '\n'; + sql << "FROM " << from << '\n'; + + TString where; tree2sql(where); + sql << where << ";"; + + set(F_SQL, sql); +} + +static int select_callback(void* jolly, int argc, char** value, char** field) +{ + TArray_sheet* sht = (TArray_sheet*)jolly; + TToken_string row; + for (int i = 0; i < argc; i++) + row.add(value[i], i); + sht->add(row); + return SQLITE_OK; +} + +void TQuery_mask::edit_query() +{ + TSQL_query qry(get(F_SQL)); + TQuery_sheet sht(qry); + sht.run(); +} + +void TQuery_mask::export(const char* ext) +{ + xvt_fsys_save_dir(); + TFilename path; path.tempdir(); + + FILE_SPEC fs; + xvt_fsys_convert_str_to_dir(path, &fs.dir); + strcpy(fs.type, ext); + strcpy(fs.name, get(F_CODICE)); + strcpy(fs.creator, "AGA"); + const bool good = xvt_dm_post_file_save(&fs, TR("Esporta")) == FL_OK; + xvt_fsys_restore_dir(); + + if (good) + { + xvt_fsys_convert_dir_to_str(&fs.dir, path.get_buffer(), path.size()); + path.add(fs.name); + + TSQL_query qry(get(F_SQL)); + if (qry.save_as(path)) + xvt_sys_goto_url(path, "open"); + } +} + +void TQuery_mask::get_sql_path(TFilename& path) const +{ + path = firm2dir(-1); + path.add("sql"); + if (!path.exist()) + make_dir(path); +} + +bool TQuery_mask::get_qry_path(TFilename& path) const +{ + const TString& name = get(F_CODICE); + if (name.not_empty()) + { + get_sql_path(path); + path.add(name); + path.ext("qry"); + } + return name.not_empty(); +} + +bool TQuery_mask::select_query() +{ + TEdit_field& fld = efield(F_CODICE); + + TFilename dirname; + get_sql_path(dirname); + + xvt_fsys_save_dir(); + + FILE_SPEC fs; + xvt_fsys_convert_str_to_dir(dirname, &fs.dir); + + strcpy(fs.type, "qry"); + strcpy(fs.name, "*"); + strcpy(fs.creator, "AGA"); + + const bool good = xvt_dm_post_file_open(&fs, (char*)fld.prompt()) == FL_OK; + + xvt_fsys_restore_dir(); + + if (good) + { + char name[_MAX_FNAME]; + xvt_fsys_parse_pathname (fs.name, NULL, NULL, name, NULL, NULL); + fld.set(name); + } + return good; +} + + +static bool xml_save_tree_handler(TTree& tree, void* jolly, word flags) +{ + TXmlItem* rel = (TXmlItem*)jolly; + TRelation_node& node = (TRelation_node&)*tree.curr_node(); + + TXmlItem& son = rel->AddChild("table"); + TString4 num; num << node.num(); + son.SetAttr("Num", num); + son.SetAttr("Name", node.name()); + if (node.alias().not_empty()) + son.SetAttr("Alias", node.alias()); + if (node.father() != NULL) + { + son.SetAttr("Father", node.father()->id()); + FOR_EACH_ARRAY_ROW(node.join(), i, row) + { + if (i > 0) + son << "AND"; + son << "(" << *row << ")"; + } + } + return false; +} + +bool TQuery_mask::save_tables_tree(TXmlItem& xml) +{ + const bool ok = _tree.goto_root(); + if (ok) + { + TXmlItem& rel = xml.AddChild("tables"); + _tree.scan_depth_first(xml_save_tree_handler, &rel); + } + return ok; +} + +bool TQuery_mask::save_fields_sheet(TXmlItem& xml) +{ + TSheet_field& sf = sfield(F_SHEET); + const bool ok = sf.items() > 0; + if (ok) + { + TXmlItem& fields = xml.AddChild("fields"); + FOR_EACH_SHEET_ROW(sf, i, row) + { + TXmlItem& field = fields.AddChild("field"); + field.SetAttr("Table", row->get(0)); + field.SetAttr("Name", row->get()); + const char* str = row->get(); + if (str && *str > ' ') + field.SetAttr("Expr", str); + } + } + return ok; +} + +bool TQuery_mask::save_query() +{ + if (_curr_query.empty() && !field(F_CODICE).empty()) + get_qry_path(_curr_query); + if (_curr_query.empty()) + return field(F_CODICE).on_key(K_ENTER); // Segnala errore + + char name[_MAX_FNAME]; + xvt_fsys_parse_pathname (_curr_query, NULL, NULL, name, NULL, NULL); + const bool ok = *name > ' '; + if (ok) + { + TXmlItem xml; + xml.SetTag("query"); + xml.SetAttr("Name", name); + xml.SetAttr("Description", get(F_DESCR)); + save_tables_tree(xml); + save_fields_sheet(xml); + xml.AddChild("sql") << get(F_SQL); + xml.Save(_curr_query); + _is_dirty = false; + } + + return ok; +} + +bool TQuery_mask::save_if_needed() +{ + if (!_is_dirty) + return true; + if (!yesno_box(TR("Si desidera registrare la query?"))) + return false; + + return save_query(); +} + +/////////////////////////////////////////////////////////// +// Caricamento da file xml +/////////////////////////////////////////////////////////// + +// Carica l'albero della relazione +bool TQuery_mask::load_tables_tree(TXmlItem& tables) +{ + for (int i = 0; i < tables.GetChildren(); i++) + { + const TXmlItem& table = *tables.GetChild(i); + const int num = atoi(table.GetAttr("Num")); + if (num >= LF_USER) + { + _tree.find_id(table.GetAttr("Father")); + const TRelation_node* father = (const TRelation_node*)_tree.curr_node(); + TRelation_node* son = new TRelation_node(father, num, table.GetAttr("Alias")); + if (father != NULL) + { + TString expr; table.GetEnclosedText(expr); + int i = expr.find('('); + while (i >= 0) + { + const int j = expr.find(')', i+1); + TToken_string* eq = new TToken_string(expr.sub(i+1, j), '='); + son->join().add(eq); + i = expr.find('(', j+1); + } + } + _tree.add_son(son); + } + } + + const bool ok = _tree.goto_root(); + if (ok) + { + const TRelation_node* rn = (TRelation_node*)_tree.curr_node(); + _curr_num = rn->num(); + fill_fields(); + _tree.expand_all(); + tfield(F_TABLES).win().force_update(); + } + enable_field_buttons(); + + return ok; +} + +// Carica l'elenco dei campi (o colonne) +bool TQuery_mask::load_fields_sheet(TXmlItem& xml) +{ + TSheet_field& sheet = sfield(F_SHEET); + TXmlItem* fields = xml.FindFirst("fields"); + const bool ok = fields != NULL; + if (ok) + { + for (int i = 0; i < fields->GetChildren(); i++) + { + const TXmlItem& field = *fields->GetChild(i); + TToken_string& row = sheet.row(-1); + row.add(field.GetAttr("Table")); + row.add(field.GetAttr("Name")); + row.add(field.GetAttr("Expr")); + } + sheet.force_update(); + } + enable_sql_button(); + return ok; +} + +// Azzera tutto, ma proprio tutto +void TQuery_mask::global_reset() +{ + if (_tree.goto_root()) + { + _tree.kill_node(); + tfield(F_TABLES).win().force_update(); + } + reset(); +} + +// Caica l'intera query +bool TQuery_mask::load_query() +{ + TFilename path; get_qry_path(path); + bool ok = path.exist(); + if (ok) + { + ifstream qry(path); + TXmlItem xml; + ok = xml.Read(qry); + if (ok) + { + _curr_query = path; + global_reset(); + set(F_CODICE, xml.GetAttr("Name")); + set(F_DESCR, xml.GetAttr("Description")); + + TXmlItem* tables = xml.FindFirst("tables"); + if (tables != NULL) + load_tables_tree(*tables); + + load_fields_sheet(xml); + + const TXmlItem* sql = xml.FindFirst("sql"); + if (sql != NULL) + { + TString str; sql->GetEnclosedText(str); + set(F_SQL, str); + } + + _is_dirty = false; + } + } + return ok; +} + +bool TQuery_mask::delete_query() +{ + TFilename path; get_qry_path(path); + const bool ok = yesno_box(FR("Si desidera eliminare il file %s"), (const char*)path); + if (ok) + { + ::remove(path); + global_reset(); + } + return ok; +} + +// Gestione eventi standard +bool TQuery_mask::on_field_event(TOperable_field& o, TField_event e, long jolly) +{ + switch (o.dlg()) + { + case F_CODICE: + if (e == fe_button) + { + if (select_query()) + e = fe_modify; + } + if (e == fe_modify) + { + save_if_needed(); + load_query(); + get_qry_path(_curr_query); + } + break; + case F_TABLES: + if (e == fe_modify) + { + const TRelation_node* rn = (TRelation_node*)_tree.curr_node(); + if (rn && rn->num() != _curr_num) + { + _curr_num = rn->num(); + fill_fields(); + } + } + break; + case F_FIELDS: + if (e == se_query_modify || e == se_query_add || e == se_query_del) + return false; + enable_field_buttons(); + break; + case F_ADDFILE: + if (e == fe_button) + add_file_to_tree(); + break; + case F_EDITFILE: + if (e == fe_button) + edit_file_in_tree(); + break; + case DLG_USER: + if (e == fe_button) + add_field_to_sheet(); + break; + case F_ASTERISK: + if (e == fe_button) + add_asterisk_to_sheet(); + break; + case F_GENSQL: + if (e == fe_button) + { + sheet2sql(); + next_page(1001); + } + break; + case F_EDITQUERY: + if (e == fe_button) + edit_query(); + break; + case F_EXPORT_HTML: + if (e == fe_button) + export("html"); + break; + case F_EXPORT_EXCEL: + if (e == fe_button) + export("xls"); + break; + case F_EXPORT_TXT: + if (e == fe_button) + export("txt"); + break; + case F_SHEET: + enable_sql_button(); + break; + case DLG_NEWREC: + if (e == fe_button) + { + save_if_needed(); + global_reset(); + } + break; + case DLG_FINDREC: + if (e == fe_button) + send_key(K_F9, F_CODICE); + case DLG_SAVEREC: + if (e == fe_button) + save_query(); + break; + case DLG_DELREC: + if (e == fe_button && jolly == 0) // Elimina della Toolbar + { + delete_query(); + return false; // Do not exit! + } + break; + default: + break; + } + return true; +} + +// Gestione eventi di trascinamento +void TQuery_mask::handler(WINDOW wnd, EVENT* ep) +{ + switch (ep->type) + { + case E_MOUSE_DOWN: + if (ep->v.mouse.button == 0 && wnd == win(0)) + { + const TSheet_field& sf = sfield(F_FIELDS); + RCT rct; sf.get_rect(rct); + _dragster = xvt_rect_has_point(&rct, ep->v.mouse.where) != 0; + } + else + _dragster = false; + if (_dragster) + { + XinCursor hand = xi_get_pref(XI_PREF_HAND_CURSOR_RID); + xvt_win_set_cursor(wnd, (CURSOR)hand); + } + else + xvt_win_set_cursor(wnd, CURSOR_ARROW); + break; + case E_MOUSE_UP: + if (ep->v.mouse.button == 0 && _dragster) + { + TSheet_field& ff = sfield(F_SHEET); + RCT rct; ff.get_rect(rct); + if (xvt_rect_has_point(&rct, ep->v.mouse.where)) + add_field_to_sheet(); + xvt_win_set_cursor(wnd, CURSOR_ARROW); + } + _dragster = false; + break; + default: + break; + } +} + +TQuery_mask::TQuery_mask() : TAutomask("ba8200a"), _curr_num(0), _is_dirty(false) +{ + RCT rcts, rctf, rctt; + + TSheet_field& sheet = sfield(F_SHEET); + sheet.get_rect(rcts); + + // Allarga a dritta lo spreadsheet coi noi dei campi + TSheet_field& fields = sfield(F_FIELDS); + fields.get_rect(rctf); + rctf.right = rcts.right; + fields.set_rect(rctf); + + // Allarga a mancina l'albero della relazione + TTree_field& trf = tfield(F_TABLES); + trf.get_rect(rctt); + rctt.top -= 4; + rctt.left = rcts.left+4; + rctt.right -= 32; // Toglie scrollbar + rctt.bottom = rctf.bottom - 32; + trf.set_rect(rctt); + + trf.set_tree(&_tree); // Associa l'albero al campo della maschera +} + +/////////////////////////////////////////////////////////// +// TSql_app +/////////////////////////////////////////////////////////// + +class TSql_app : public TSkeleton_application +{ + TQuery_mask* _msk; + +public: + virtual bool create(); + virtual void main_loop(); + virtual bool destroy(); +}; + +void TSql_app::main_loop() +{ + _msk->run(); +} + +bool TSql_app::create() +{ + _msk = new TQuery_mask; + return TSkeleton_application::create(); +} + +bool TSql_app::destroy() +{ + delete _msk; + return TSkeleton_application::destroy(); +} + + +int ba8200(int argc, char* argv[]) +{ + TSql_app app; + app.run(argc, argv, "Query"); + return 0; +} diff --git a/ba/ba8200.h b/ba/ba8200.h new file mode 100755 index 000000000..b649bfd82 --- /dev/null +++ b/ba/ba8200.h @@ -0,0 +1,19 @@ +#define F_CODICE 150 +#define F_DESCR 151 +#define F_SQL 152 +#define F_TABLES 200 +#define F_FIELDS 300 +#define F_SHEET 400 +#define F_ADDFILE 301 +#define F_EDITFILE 302 +#define F_ASTERISK 303 +#define F_GENSQL 304 +#define F_EDITQUERY 305 +#define F_EXPORT_HTML 306 +#define F_EXPORT_EXCEL 307 +#define F_EXPORT_TXT 308 + +#define F_FATHER 201 +#define F_FATHER_ALIAS 202 +#define F_SON 203 +#define F_SON_ALIAS 204 diff --git a/ba/ba8200a.uml b/ba/ba8200a.uml new file mode 100755 index 000000000..04ce9c921 --- /dev/null +++ b/ba/ba8200a.uml @@ -0,0 +1,215 @@ +#include "ba8200.h" + +TOOLBAR "" 0 -2 0 2 + +BUTTON DLG_SAVEREC 10 2 +BEGIN + PROMPT -15 -11 "" + PICTURE BMP_SAVEREC + PICTURE BMP_SAVERECDN +END + +BUTTON DLG_NEWREC 10 2 +BEGIN + PROMPT -25 -11 "~Nuovo" + PICTURE BMP_NEWREC + PICTURE BMP_NEWRECDN +END + +BUTTON DLG_DELREC 10 2 +BEGIN + PROMPT -35 -11 "" + PICTURE BMP_DELREC + PICTURE BMP_DELRECDN +END + +BUTTON DLG_FINDREC 10 2 +BEGIN + PROMPT -45 -11 "Ri~cerca" + PICTURE BMP_FINDREC +END + +BUTTON DLG_QUIT 10 2 +BEGIN + PROMPT -55 -11 "" +END + +ENDPAGE + +PAGE "Query" -1 -1 78 23 + +STRING F_CODICE 16 +BEGIN + PROMPT 1 0 "Codice " + FLAGS "B" + WARNING "E' necessario specificare il codice" + CHECKTYPE REQUIRED +END + +STRING F_DESCR 50 +BEGIN + PROMPT 29 0 "" +END + +TREE F_TABLES 30 8 +BEGIN + PROMPT 1 1 "" +END + +SPREADSHEET F_FIELDS 30 10 +BEGIN + PROMPT 34 1 "" + ITEM "Nome@12F" + ITEM "Dim." + ITEM "Chiavi@6" + ITEM "Descrizione@50" + ITEM "Peso" +END + +BUTTON F_ADDFILE 10 2 +BEGIN + PROMPT -16 11 "~Tabella" +END + +BUTTON F_EDITFILE 10 2 +BEGIN + PROMPT -26 11 "~Edit" + PICTURE BMP_EDIT +END + +BUTTON DLG_USER 10 2 +BEGIN + PROMPT -46 11 "~Campo" +END + +BUTTON F_ASTERISK 10 2 +BEGIN + PROMPT -56 11 "Campo ~*" +END + +BUTTON F_GENSQL 10 2 +BEGIN + PROMPT -66 11 "~SQL" +END + +SPREADSHEET F_SHEET +BEGIN + PROMPT 0 13 "" + ITEM "Tabella@12F" + ITEM "Colonna@32F" + ITEM "Condizione@50" +END + +ENDPAGE + +PAGE "Sql" -1 -1 78 23 + +MEMO F_SQL 78 -3 +BEGIN + PROMPT 1 1 "SQL query" +END + +BUTTON F_EDITQUERY 10 2 +BEGIN + PROMPT -15 -1 "" + PICTURE BMP_EDIT +END + +BUTTON F_EXPORT_HTML 10 2 +BEGIN + PROMPT -25 -1 "~HTML" +END + +BUTTON F_EXPORT_EXCEL 10 2 +BEGIN + PROMPT -35 -1 "~Excel" +END + +BUTTON F_EXPORT_TXT 10 2 +BEGIN + PROMPT -45 -1 "~Testo+Tab" +END + +ENDPAGE + +ENDMASK + +PAGE "CampiFile" -1 -1 64 8 + +STRING 101 12 +BEGIN + PROMPT 1 1 "Nome " +END + +NUMBER 102 2 +BEGIN + PROMPT 35 1 "Dimensioni " +END + +STRING 103 20 +BEGIN + PROMPT 1 2 "Chiavi " +END + +MEMO 104 60 3 +BEGIN + PROMPT 1 3 "Descrizione " +END + +NUMBER 105 4 +BEGIN + PROMPT 53 2 "Peso " +END + + +BUTTON DLG_CANCEL 10 2 +BEGIN + PROMPT -12 -1 "" +END + +BUTTON DLG_USER 10 2 +BEGIN + PROMPT -22 -1 "Aggiungi" +END + +ENDPAGE + +ENDMASK + +PAGE "CampiQuery" -1 -1 60 13 + +STRING 101 12 +BEGIN + PROMPT 1 1 "Tabella " + CHECKTYPE REQUIRED +END + +STRING 102 32 +BEGIN + PROMPT 31 1 "Colonna " + CHECKTYPE REQUIRED +END + +MEMO 103 54 8 +BEGIN + PROMPT 1 2 "Condizione " +END + +BUTTON DLG_CANCEL 10 2 +BEGIN + PROMPT -13 -1 "" +END + +BUTTON DLG_DELREC 10 2 +BEGIN + PROMPT -23 -1 "" +END + +BUTTON DLG_OK 10 2 +BEGIN + PROMPT -33 -1 "" +END + +ENDPAGE + +ENDMASK diff --git a/ba/ba8200b.h b/ba/ba8200b.h new file mode 100755 index 000000000..e69de29bb diff --git a/ba/ba8200b.uml b/ba/ba8200b.uml new file mode 100755 index 000000000..867f72d50 --- /dev/null +++ b/ba/ba8200b.uml @@ -0,0 +1,95 @@ +#include "ba8200.h" + +PAGE "Nuova tabella" -1 -1 50 15 + +GROUP DLG_NULL 24 4 +BEGIN + PROMPT 1 0 "@bTabella collegata" +END + +STRING F_SON 8 +BEGIN + PROMPT 2 1 "Tabella " + FLAGS "U" + SHEET "Tabella|Num@3R|Descrizione archivio@70" + INPUT F_SON + OUTPUT F_SON + CHECKTYPE REQUIRED +END + +STRING F_SON_ALIAS 12 +BEGIN + PROMPT 2 2 "Alias " + FLAGS "U" +END + +GROUP DLG_NULL 24 4 +BEGIN + PROMPT 25 0 "@bTabella principale" +END + +STRING F_FATHER 8 +BEGIN + PROMPT 26 1 "Tabella " + FLAGS "D" +END + +STRING F_FATHER_ALIAS 12 +BEGIN + PROMPT 26 2 "Alias " + FLAGS "D" +END + +SPREADSHEET F_SHEET 0 -3 +BEGIN + PROMPT 1 4 "" + ITEM "Campo@16" + ITEM "Campo@16" +END + +BUTTON DLG_OK 10 2 +BEGIN + PROMPT -13 -1 "" +END + +BUTTON DLG_DELREC 10 2 +BEGIN + PROMPT -23 -1 "" +END + +BUTTON DLG_CANCEL 10 2 +BEGIN + PROMPT -33 -1 "" +END + +ENDPAGE + +ENDMASK + +PAGE "Fields" -1 -1 68 6 + +STRING 101 12 +BEGIN + PROMPT 1 1 "Campo " + FLAGS "D" +END + +STRING 102 12 +BEGIN + PROMPT 1 2 "Campo " +END + +BUTTON DLG_OK 10 2 +BEGIN + PROMPT -12 -1 "" +END + +BUTTON DLG_CANCEL 10 2 +BEGIN + PROMPT -22 -1 "" +END + +ENDPAGE + +ENDMASK + diff --git a/ba/ba8201.cpp b/ba/ba8201.cpp new file mode 100755 index 000000000..65f185c74 --- /dev/null +++ b/ba/ba8201.cpp @@ -0,0 +1,692 @@ +#include + +#include +#include +#include +#include +#include + +#include "ba8201.h" + +/////////////////////////////////////////////////////////// +// Private interface +/////////////////////////////////////////////////////////// + +#include "../../sqlite/sources/sqlite.h" + +class TSQLite : public TObject +{ + sqlite* _handle; + TFilename _currdb; + TAssoc_array _names; + +protected: + void logicnum2name(int logicnum, TString& name) const; + int name2logicnum(const TString& name) const; + + TString& get_sql_value(const TRectype& curr, const RecFieldDes& fd, TString& tmp) const; + + void build_curr_path(TFilename& name) const; + void test_path(); + + bool export(const TRectype& rec, ostream& sql) const; + bool create_dbf_times(); + long get_dbf_time(const TString& table); + bool set_dbf_time(const TString& table, long last); + bool import(int logicnum); + +public: + sqlite* open(const char* fname = NULL); + bool exec(const char* sql, sqlite_callback callback = NULL, void* jolly = NULL, bool show_error = true); + void close(); + + bool exists(const char* table); + bool parse_select_from(const char* szSql); + + TSQLite(); + virtual ~TSQLite(); +} _TheDataBase; + +void TSQLite::build_curr_path(TFilename& name) const +{ + name = firm2dir(-1); + name.add("sql"); + if (!name.exist()) + make_dir(name); + + TString16 firm; + firm.format("%05ldA.sql", prefix().get_codditta()); + name.add(firm); +} + +sqlite* TSQLite::open(const char* fname) +{ + close(); + _currdb = fname; + char* errmsg = NULL; + _handle = sqlite_open(_currdb, 0, &errmsg); + if (errmsg != NULL) + { + error_box(errmsg); + sqlite_freemem(errmsg); + } + + create_dbf_times(); + + return _handle; +} + +void TSQLite::test_path() +{ + TFilename n; + build_curr_path(n); + if (n != _currdb) + open(n); +} + +bool TSQLite::exec(const char* sql, sqlite_callback callback, void* jolly, bool show_error) +{ + TWait_cursor hourglass; + char* errmsg = NULL; + const int rc = sqlite_exec(_handle, sql, callback, jolly, &errmsg); + if (errmsg != NULL) + { + if (show_error) + { + TString msg; + msg << sql; + msg.cut(128); + msg << '\n' << errmsg; + error_box(msg); + } + sqlite_freemem(errmsg); + } + return rc == SQLITE_OK; +} + +void TSQLite::close() +{ + if (_handle != NULL) + { + sqlite_close(_handle); + _handle = NULL; + } +} + +const char* const DBF_TIMES_TABLE = "DBF_TIMES"; + +bool TSQLite::create_dbf_times() +{ + bool ok = exists(DBF_TIMES_TABLE); + if (!ok) + { + TString sql; + sql << "CREATE TABLE " << DBF_TIMES_TABLE << " (name,time);\n" + << "CREATE UNIQUE INDEX " << DBF_TIMES_TABLE << "_1 ON " << DBF_TIMES_TABLE << " (name);"; + ok = exec(sql); + } + return ok; +} + +bool TSQLite::set_dbf_time(const TString& table, long last) +{ + TString sql; +// sql << "INSERT OR REPLACE INTO " << DBF_TIMES_TABLE << " VALUES(" + sql << "REPLACE INTO " << DBF_TIMES_TABLE << " VALUES(" + << '\'' << table << "','" << last << "');"; + return exec(sql); +} + +static int dbf_time_callback(void* jolly, int argc, char** argv, char** columns) +{ + long& last = *(long*)jolly; + last = atol(argv[0]); + return SQLITE_OK; +} + +long TSQLite::get_dbf_time(const TString& table) +{ + TString sql; + sql << "SELECT time FROM " << DBF_TIMES_TABLE << " WHERE name='" << table << "';"; + long last = 0; + exec(sql, dbf_time_callback, &last); + return last; +} + +void TSQLite::logicnum2name(int logicnum, TString& name) const +{ + const FileDes& fd = prefix().get_filedes(logicnum); + const TFilename n = fd.SysName; + name = n.name(); name.upper(); +} + +int TSQLite::name2logicnum(const TString& name) const +{ + TString* cod = (TString*)_names.objptr(name); + if (cod == NULL) + { + FileDes dir; + CGetFile(LF_DIR, &dir, _nolock, NORDIR); + const int nfiles = (int)dir.EOD; + + TString8 n; + for (int logic = LF_USER; logic < nfiles; logic++) + { + logicnum2name(logic, n); + cod = new TString4; cod->format("%d", logic); + ((TAssoc_array&)_names).add(n, cod); + } + + cod = (TString*)_names.objptr(name); + if (cod == NULL) // Continua ostinatamente a non esistere? + return 0; + } + return atoi(*cod); +} + +TString& TSQLite::get_sql_value(const TRectype& curr, const RecFieldDes& fd, TString& tmp) const +{ + tmp = curr.get(fd.Name); + switch (fd.TypeF) + { + case _intfld: + case _longfld: + case _realfld: + case _wordfld: + if (real::is_null(tmp)) + tmp.cut(0); + break; + case _intzerofld: + case _longzerofld: + break; + case _datefld: + tmp = TDate(tmp).string(ANSI); + break; + case _boolfld: + break; + default: +/* + if (tmp.find('\'') >= 0) + { + for (int i = 0; tmp[i]; i++) + { + if (tmp[i] == '\'') + tmp.insert("'", i++); + } + } +*/ + break; + } + return tmp; +} + +static int exists_callback(void *jolly, int argc, char **argv, char **azColName) +{ + bool& yes = *(bool*)jolly; + yes = argc > 0; + return SQLITE_OK; +} + +bool TSQLite::exists(const char* table) +{ + TString sql; + sql << "SELECT name FROM sqlite_master WHERE (type='table')AND(name='" << table << "');"; + bool yes = false; + exec(sql, exists_callback, &yes, false); + return yes; +} + +bool TSQLite::export(const TRectype& rec, ostream& sql) const +{ + const RecDes& rd = *rec.rec_des(); + TString tmp; + for (int i = 0; i < rd.NFields; i++) + { + if (i > 0) sql << '\t'; + if (rd.Fd[i].TypeF == _memofld) + sql << "\\N"; + else + sql << get_sql_value(rec, rd.Fd[i], tmp); + } + sql << '\n'; + return true; +} + +bool TSQLite::import(int logicnum) +{ + TString table; logicnum2name(logicnum, table); + + long last = get_dbf_time(table); + if (logicnum >= LF_USER) // Dummy test + { + TLocalisamfile file(logicnum); + if (!file.is_changed_since(last)) + return true; + } + + const RecDes& rd = prefix().get_recdes(logicnum); + + TString sql; + if (exists(table)) + { + // Drop old table + sql.cut(0) << "DROP TABLE "<< table << ';'; + exec(sql); + } + + // Create new table + sql.cut(0) << "CREATE TABLE "<< table << "\n("; + for (int i = 0; i < rd.NFields; i++) + { + if (i > 0) sql << ','; + sql << rd.Fd[i].Name; + } + sql << ");"; + if (!exec(sql)) + return false; + + // Create main index +// sql.cut(0) << "CREATE UNIQUE INDEX " << table << "_1 ON "<< table << "\n("; + sql.cut(0) << "CREATE INDEX " << table << "_1 ON "<< table << "\n("; + const KeyDes& kd = rd.Ky[0]; + for (int k = 0; k < kd.NkFields; k++) + { + if (k > 0) sql << ','; + const int ndx = kd.FieldSeq[k] % MaxFields; + sql << rd.Fd[ndx].Name; + } + sql << ");"; + if (!exec(sql)) + return false; + + + TRelation rel(logicnum); + TCursor cur(&rel); + const TRecnotype items = cur.items(); + cur.freeze(); + const TRectype& curr = rel.curr(); + + TString msg; + msg << TR("Esportazione tabella") << ' ' << table; + msg << ": " << items << ' ' << TR("righe"); + TProgind pi(items, msg, true, true); + + TFilename tmp; tmp.tempdir(); tmp.add("sql.txt"); + ofstream txt(tmp, ios::binary); + + for (cur = 0; cur.pos() < items; ++cur) + { + export(curr, txt); + pi.addstatus(1); + if (pi.iscancelled()) + break; + } + + txt.close(); + + msg << '\n' << TR("Importazione tabella") << ' ' << table; + msg << ": " << items << ' ' << TR("righe"); + pi.set_text(msg); + + sql.cut(0) << "COPY " << table << " FROM '" << tmp << "';"; + if (exec(sql)) + set_dbf_time(table, last); // Aggiorna ora di ultima modifica + + ::remove(tmp); + + return true; +} + +bool TSQLite::parse_select_from(const char* szSql) +{ + test_path(); + + TString sql(szSql); + sql.trim(); + if (!sql.starts_with("SELECT")) + return false; + + const int from = sql.find("FROM"); + if (from < 0) + return false; + + const int where = sql.find("WHERE", from); + TToken_string tables(sql.sub(from+5, where), ','); + TString table; + FOR_EACH_TOKEN(tables, tok) + { + table = tok; + table.trim(); + for (int i = 0; table[i]; i++) + { + if (table[i] <= ' ' || table[i] == ';') + { table.cut(i); break; } + } + const int logicnum = name2logicnum(table); + if (logicnum > 0) + import(logicnum); + } + + return true; +} + +TSQLite::TSQLite() : _handle(NULL) +{ +} + +TSQLite::~TSQLite() +{ + close(); +} + +/////////////////////////////////////////////////////////// +// TSQL_query +/////////////////////////////////////////////////////////// + +void TSQL_query::reset() +{ + _firstrow = _items = 0; + _pagesize = 256; + _page.destroy(); + _column.destroy(); +} + +int TSQL_query::on_get_items(int argc, char** values, char** columns) +{ + if (_column.items() == 0) + { + for (int i = 0; i < argc; i++) + { + TSQL_column_info* info = new TSQL_column_info; + info->_name = columns[i]; + info->_width = 1; + info->_numeric = true; + _column.add(info); + } + } + if (_items < _pagesize) + { + for (int i = 0; i < argc; i++) if (values[i] && *values[i]) + { + TSQL_column_info& info = (TSQL_column_info&)_column[i]; + const int len = strlen(values[i]); + if (len > info._width) + info._width = len; + if (isalpha(*values[i])) + info._numeric = false; + } + } + + _items++; + return SQLITE_OK; +} + +static int query_get_items(void* jolly, int argc, char** values, char** columns) +{ + TSQL_query* q = (TSQL_query*)jolly; + q->on_get_items(argc, values, columns); + return SQLITE_OK; +} + +TRecnotype TSQL_query::items() const +{ + if (_items == 0) + _TheDataBase.exec(_sql, query_get_items, (TSQL_query*)this); + return _items; +} + +unsigned int TSQL_query::columns() const +{ + if (_column.items() == 0) + items(); + return _column.items(); +} + +const TSQL_column_info& TSQL_query::column_info(unsigned int c) const +{ + return (const TSQL_column_info&)_column[c]; +} + +static int query_get_rows(void* jolly, int argc, char** values, char** columns) +{ + TArray& arr = *(TArray*)jolly; + TString_array* a = new TString_array; + for (int i = 0; i < argc; i++) + a->add(values[i], i); + arr.add(a); + return SQLITE_OK; +} + +const TString_array* TSQL_query::row(TRecnotype n) +{ + if (n < _firstrow || n >= _firstrow+_page.items()) + { + TString sql = _sql; + if (sql.find("LIMIT ") < 0) + { + const int semicolon = sql.rfind(';'); + if (semicolon >= 0) + sql.cut(semicolon); + sql.trim(); + _page.destroy(); + _firstrow = n; + sql << "\nLIMIT " << _pagesize << " OFFSET " << _firstrow << ';'; + } + _TheDataBase.exec(sql, query_get_rows, &_page); + } + + return (const TString_array*)_page.objptr(n-_firstrow); +} + +void TSQL_query::set(const char* sql) +{ + reset(); + _sql = sql; + if (_sql.find("SELECT") >= 0) + _TheDataBase.parse_select_from(_sql); +} + +const TToken_string& TSQL_query::sheet_head() const +{ + TToken_string& head = get_tmp_string(); + TToken_string tablefield(32, '.'); + for (unsigned int c = 0; c < columns(); c++) + { + const TSQL_column_info& ci = column_info(c); + tablefield = ci._name; + int maxlen = 0; + FOR_EACH_TOKEN(tablefield, tok) + { + if (maxlen == 0) + head.add(tok); + else + head << '\n' << tok; + const int len = strlen(tok); + if (len > maxlen) + maxlen = len; + } + head << '@' << max(ci._width, maxlen); + if (ci._numeric) + head << 'R'; + } + return head; +} + +bool TSQL_query::save_as_html(const TFilename& path) +{ + TProgind pi(items(), TR("Esportazione in corso..."), true, true); + + ofstream out(path); + out << "" << endl; + out << "" << endl; + out << "" << endl; + out << " " << endl; + + out << " "; + for (unsigned int c = 0; c < columns(); c++) + { + const TSQL_column_info& ci = column_info(c); + TToken_string header(ci._name, '.'); + TString str; + FOR_EACH_TOKEN(header, tok) + { + if (str.not_empty()) + str << "
"; + str << tok; + } + out << " " << endl; + } + out << " " << endl; + + for (TRecnotype n = 0; n < items(); n++) + { + pi.addstatus(1); + if (pi.iscancelled()) + break; + const TString_array* arr = row(n); + const int last = arr != NULL ? arr->last() : 0; + out << " " << endl; + for (int c = 0; c <= last; c++) + { + const TSQL_column_info& ci = column_info(c); + out << " "; + TString* val = (TString*)arr->objptr(c); + if (val && !val->blank()) + { + val->rtrim(); + out << *val; + } + out << " " << endl; + } + out << " " << endl; + } + + out << "
" << _sql << "
" << str << "
" << endl; + out << "" << endl; + out << "" << endl; + + return !pi.iscancelled(); +} + +bool TSQL_query::save_as_silk(const TFilename& path) +{ + TProgind pi(items(), TR("Esportazione in corso..."), true, true); + + ofstream out(path); + out << "ID;PWXL;N;E" << endl; + + for (unsigned int c = 0; c < columns(); c++) + { + const TSQL_column_info& ci = column_info(c); + out << "C;Y1;X" << (c+1) << ";K\"" << ci._name << '"' << endl; + } + for (TRecnotype n = 0; n < items(); n++) + { + pi.addstatus(1); + if (pi.iscancelled()) + break; + const TString_array* arr = row(n); + const int last = arr != NULL ? arr->last() : 0; + for (int c = 0; c <= last; c++) + { + out << "C;Y" << (n+2) << ";X" << (c+1) << ";K\""; + TString* val = (TString*)arr->objptr(c); + if (val && !val->blank()) + { + val->rtrim(); + val->replace('"', '\''); + out << *val; + } + out << '"' << endl; + } + } + out << "E" << endl; + + return !pi.iscancelled(); +} + +bool TSQL_query::save_as_text(const TFilename& path) +{ + TProgind pi(items(), TR("Esportazione in corso..."), true, true); + + ofstream out(path); + for (TRecnotype n = 0; n < items(); n++) + { + const TString_array* arr = row(n); + const int last = arr != NULL ? arr->last() : 0; + for (int c = 0; c <= last; c++) + { + TString* val = (TString*)arr->objptr(c); + if (val && !val->blank()) + { + val->rtrim(); + out << *val; + } + out << '\t'; + } + out << endl; + pi.addstatus(1); + if (pi.iscancelled()) + break; + } + + return !pi.iscancelled(); +} + +bool TSQL_query::save_as(const char* fn, TQueryExportFormat fmt) +{ + TFilename path(fn); + TString ext = path.ext(); ext.lower(); + if (fmt == fmt_unknown) + { + if (ext.starts_with("htm") || ext == "xml") + fmt = fmt_html; else + if (ext == "slk" || ext == "xls") + fmt = fmt_slk; else + if (ext == "txt") + fmt = fmt_txt; + } + bool ok = fmt != fmt_unknown; + switch (fmt) + { + case fmt_html: ok = save_as_html(path); break; + case fmt_slk: ok = save_as_silk(path); break; + case fmt_txt: ok = save_as_text(path); break; + default: break; + } + return ok; +} + +TSQL_query::TSQL_query(const char* sql) +{ + set(sql); +} + +/////////////////////////////////////////////////////////// +// TQuery_sheet +/////////////////////////////////////////////////////////// + +void TQuery_sheet::get_row(long r, TToken_string& row) +{ + row.separator('\t'); + row.cut(0); + if (r >= 0 && r < items()) + { + TString_array& arr = (TString_array&)*_query.row(r); + FOR_EACH_ARRAY_ROW(arr, i, str) + row.add(*str); + } +} + +long TQuery_sheet::get_items() const +{ + return _query.items(); +} + +TQuery_sheet::TQuery_sheet(TSQL_query& query) + : TSheet(-1, -1, -2, -4, "Query", query.sheet_head()), _query(query) +{ +} diff --git a/ba/ba8201.h b/ba/ba8201.h new file mode 100755 index 000000000..9c9da3a47 --- /dev/null +++ b/ba/ba8201.h @@ -0,0 +1,70 @@ +#ifndef __SQLITE_H +#define __SQLITE_H + +#ifndef __SHEET_H +#include +#endif + +/////////////////////////////////////////////////////////// +// TSQL_query +/////////////////////////////////////////////////////////// + +struct TSQL_column_info : public TObject +{ + TString _name; + int _width; + bool _numeric; +}; + +enum TQueryExportFormat { fmt_unknown, fmt_html, fmt_txt, fmt_slk }; + +class TSQL_query : public TObject +{ + TString _sql; + + TRecnotype _firstrow, _pagesize, _items; + TArray _column; + TArray _page; + +protected: + void reset(); + bool save_as_html(const TFilename& path); + bool save_as_silk(const TFilename& path); + bool save_as_text(const TFilename& path); + +public: + void set(const char* sql); + + virtual int on_get_items(int argc, char** values, char** columns); + + TRecnotype items() const; + const TString_array* row(TRecnotype n); + + unsigned int columns() const; + const TSQL_column_info& column_info(unsigned int c) const; + const TToken_string& sheet_head() const; + + bool save_as(const char* filename, TQueryExportFormat = fmt_unknown); + + TSQL_query(const char* sql); + virtual ~TSQL_query() { } +}; + +/////////////////////////////////////////////////////////// +// TQuery_sheet +/////////////////////////////////////////////////////////// + +class TQuery_sheet : public TSheet +{ + TSQL_query& _query; + +protected: + virtual void get_row(long r, TToken_string& row); + virtual long get_items() const; + +public: + TQuery_sheet(TSQL_query& sql); +}; + +#endif +