#ifdef WIN32 #include #else #include "../xvaga/incstr.h" #endif #include #include #include #include #include #include #include "ba8201.h" /////////////////////////////////////////////////////////// // Utility /////////////////////////////////////////////////////////// void get_sql_directory(TFilename& name) { name = firm2dir(-1); name.add("sql"); if (!name.exist()) make_dir(name); } bool select_sql_file(TFilename& path, const char* ext) { get_sql_directory(path); path.add("*"); path.ext(ext); TString_array files; list_files(path, files); TArray_sheet sheet(-1, -1, 78, 20, TR("Selezione"), TR("Nome@20|Descrizione@50")); TString str; FOR_EACH_ARRAY_ROW(files, i, row) { TXmlItem item; if (item.Load(*row)) { TToken_string* riga = new TToken_string; path = *row; path = path.name(); path.ext(""); riga->add(path); const TXmlItem* desc = item.FindFirst("description"); str = *row; if (desc != NULL) desc->GetEnclosedText(str); riga->add(str); sheet.add(riga); } } const bool ok = sheet.run() == K_ENTER; if (ok) { get_sql_directory(path); path.add(sheet.row(-1).get(0)); path.ext(ext); } return ok; } /////////////////////////////////////////////////////////// // Private interface /////////////////////////////////////////////////////////// #include "../sqlite/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 esporta(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 { TString16 firm; firm.format("%05ldA.sql", prefix().get_codditta()); get_sql_directory(name); 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) { if (_handle == NULL) test_path(); 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::esporta(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) { esporta(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(); sql.upper(); 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 = 512; _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 < 0 || n >= items()) return NULL; 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(); if (n >= _pagesize) _firstrow = n-_pagesize/8; // Prendo qualche riga dalla pagina precedente, per velocizzare il pagina su else _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 || _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->last(); 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++) { if (c > 0) out << '\t'; TString* val = (TString*)arr->objptr(c); if (val && !val->blank()) { val->rtrim(); out << *val; } } out << endl; pi.addstatus(1); if (pi.iscancelled()) break; } return !pi.iscancelled(); } bool TSQL_query::save_as_campo(const TFilename& path) { TProgind pi(items(), TR("Esportazione in corso..."), true, true); ofstream out(path); out << "[Head]" << endl; out << "Version=0"; for (unsigned int c = 0; c < columns(); c++) { const TSQL_column_info& ci = column_info(c); if ((c % 8) == 0) out << endl << "Fields="; else out << '|'; out << ci._name; } out << endl << endl << "[Data]" << endl; 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++) { if (c > 0) out << '|'; TString* val = (TString*)arr->objptr(c); if (val && !val->blank()) { val->rtrim(); out << *val; } } 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; case fmt_campo: ok = save_as_campo(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) { }