Files correlati : Ricompilazione Demo : [ ] Commento :correzioni dovute al passaggio al nuovo compilatore git-svn-id: svn://10.65.10.50/trunk@14698 c028cbd2-c16b-5b4b-a496-9718f37d4682
		
			
				
	
	
		
			2120 lines
		
	
	
		
			46 KiB
		
	
	
	
		
			C++
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			2120 lines
		
	
	
		
			46 KiB
		
	
	
	
		
			C++
		
	
	
		
			Executable File
		
	
	
	
	
| #include "../xvaga/incstr.h"
 | |
| 
 | |
| #include <applicat.h>
 | |
| #include <extcdecl.h>
 | |
| #include <modaut.h>
 | |
| #include <progind.h>
 | |
| #include <recset.h>
 | |
| #include <relation.h>
 | |
| #include <utility.h>
 | |
| #include <xml.h>
 | |
| 
 | |
| #include <statbar.h>
 | |
| 
 | |
| ///////////////////////////////////////////////////////////
 | |
| // TTable name converter
 | |
| ///////////////////////////////////////////////////////////
 | |
| 
 | |
| class TTable_names : public TObject
 | |
| {
 | |
|   TAssoc_array _names;
 | |
|   TArray _ids;
 | |
|   long _filled;
 | |
| 
 | |
| protected:
 | |
|   void fill();
 | |
|   void add_file(int logic, const TString& table);
 | |
| 
 | |
| public:
 | |
|   const TString& name(int logic_num);
 | |
|   int logic_num(const TString& name);
 | |
| 
 | |
|   TTable_names() : _filled(-1) { }
 | |
| } _table_names;
 | |
| 
 | |
| void TTable_names::add_file(int logic, const TString& table)
 | |
| {
 | |
|   TString* id = new TString8;
 | |
|   id->format("%d", logic);
 | |
|   _names.add(table, id);
 | |
|   _ids.add(table, logic);
 | |
| }
 | |
| 
 | |
| void TTable_names::fill()
 | |
| {
 | |
|   if (_filled != prefix().get_codditta())
 | |
|   {
 | |
|     FileDes dir;
 | |
|     CGetFile(LF_DIR, &dir, _nolock, NORDIR);
 | |
|     const int nfiles = (int)dir.EOD;
 | |
| 
 | |
|     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();
 | |
|       if (_names.objptr(n) == NULL)
 | |
|         add_file(logic, n);
 | |
|     }
 | |
|     _filled = prefix().get_codditta();
 | |
|   }
 | |
| }
 | |
| 
 | |
| int TTable_names::logic_num(const TString& n)
 | |
| {
 | |
|   // Non cambiare: n puo' essere temporaneo e pieno di spazi!
 | |
|   TString80 name = n; name.trim();
 | |
|   if (isdigit(name[0]))
 | |
|   {
 | |
|     int num = atoi(name);
 | |
|     if (name.ends_with("@"))
 | |
|       num = -num;
 | |
|     return num;
 | |
|   } 
 | |
|   
 | |
|   if (name[0] == '%' && name.len() == 4)
 | |
|     return LF_TABCOM;
 | |
| 
 | |
|   TString* str = (TString*)_names.objptr(name);
 | |
|   if (str == NULL)
 | |
|   {
 | |
|     fill();
 | |
|     str = (TString*)_names.objptr(name);
 | |
|   }
 | |
| 
 | |
|   if (str == NULL && name.len() == 3)
 | |
|     return LF_TAB;
 | |
| 
 | |
|   return str == NULL ? 0 : atoi(*str);
 | |
| }
 | |
| 
 | |
| const TString& TTable_names::name(int logic_num)
 | |
| {
 | |
|   TString* str = (TString*)_ids.objptr(logic_num);
 | |
|   if (str == NULL)
 | |
|   {
 | |
|     fill();
 | |
|     str = (TString*)_ids.objptr(logic_num);
 | |
|   }
 | |
|   return str == NULL ? (const TString&)EMPTY_STRING : *str;
 | |
| }
 | |
| 
 | |
| const TString& logic2table(int logic_num)
 | |
| { return _table_names.name(logic_num); }
 | |
| 
 | |
| int table2logic(const TString& name)
 | |
| { return _table_names.logic_num(name); }
 | |
| 
 | |
| 
 | |
| ///////////////////////////////////////////////////////////
 | |
| // Utility
 | |
| ///////////////////////////////////////////////////////////
 | |
| 
 | |
| static bool is_numeric(const char* str)
 | |
| {
 | |
|   if (str == NULL || *str == '\0' || *str == '0')
 | |
|     return false;    // Se comincia per zero va preservato!
 | |
|   if (*str == '-')
 | |
|   {
 | |
|     str++;
 | |
|     if (*str <= ' ')
 | |
|       return false;
 | |
|   }
 | |
|   while (*str)
 | |
|   {
 | |
|     if (strchr("0123456789.", *str) == NULL)
 | |
|       return false;
 | |
|     str++;
 | |
|   }
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| ///////////////////////////////////////////////////////////
 | |
| // TRecordset
 | |
| ///////////////////////////////////////////////////////////
 | |
| 
 | |
| const TString& TRecordset::query_text() const
 | |
| {
 | |
|   return EMPTY_STRING;
 | |
| }
 | |
| 
 | |
| const TToken_string& TRecordset::sheet_head() const
 | |
| {
 | |
|   TToken_string head;
 | |
|   TToken_string tablefield(32, '.');
 | |
|   for (unsigned int c = 0; c < columns(); c++)
 | |
|   {
 | |
|     const TRecordset_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._type == _wordfld || ci._type == _intfld || ci._type == _longfld || ci._type == _realfld)
 | |
|       head << 'R';
 | |
|   }
 | |
| 
 | |
|   // Creo la stringa temporanea solo ora, altrimenti puo' essere sovrascritta!
 | |
|   TToken_string& h = get_tmp_string();
 | |
|   h = head;
 | |
|   return h;
 | |
| }
 | |
| 
 | |
| bool TRecordset::save_as_html(const char* path)
 | |
| {
 | |
|   TProgind pi(items(), TR("Esportazione in corso..."), true, true);
 | |
|   ofstream out(path);
 | |
|   out << "<html>" << endl;
 | |
|   out << "<body>" << endl;
 | |
| 
 | |
|   TString qry; parsed_text(qry);
 | |
|   for (int i = qry.find('\n'); i > 0; i = qry.find('\n', i+1))
 | |
|     qry.insert("<br/>", i+1);
 | |
| 
 | |
|   out << "<p><b>" << qry << "</b></p>" << endl;
 | |
| 
 | |
|   out << "<table border=\"1\">" << endl;
 | |
|   out << " <thead>";
 | |
|   for (unsigned int c = 0; c < columns(); c++)
 | |
|   {
 | |
|     const TRecordset_column_info& ci = column_info(c);
 | |
|     TToken_string header(ci._name, '.');
 | |
|     TString str;
 | |
|     FOR_EACH_TOKEN(header, tok)
 | |
|     {
 | |
|       if (str.not_empty())
 | |
|         str << "<br/>";
 | |
|       str << tok;
 | |
|     }
 | |
|     out << "  <th>" << str << "</th>" << endl;
 | |
|   }
 | |
|   out << " </thead>" << endl;
 | |
| 
 | |
|   TString val;
 | |
|   for (TRecnotype n = 0; n < items(); n++)
 | |
|   {
 | |
|     move_to(n);
 | |
|     pi.addstatus(1);
 | |
|     if (pi.iscancelled())
 | |
|       break;
 | |
|     out << " <tr>" << endl;
 | |
|     for (unsigned int c = 0; c < columns(); c++)
 | |
|     {
 | |
|       const TRecordset_column_info& ci = column_info(c);
 | |
|       out << "  <td";
 | |
|       if (ci._type == _longfld || ci._type == _realfld)
 | |
|         out << " align=\"right\"";
 | |
|       out << ">";
 | |
|       get(c).as_string(val);
 | |
|       if (!val.blank())
 | |
|       {
 | |
|         val.rtrim();
 | |
|         out << val;
 | |
|       }
 | |
|       out << "  </td>" << endl;
 | |
|     }
 | |
|     out << " </tr>" << endl;
 | |
|   }
 | |
|   
 | |
|   out << "</table>" << endl;
 | |
|   out << "</body>" << endl;
 | |
|   out << "</html>" << endl;
 | |
| 
 | |
|   return !pi.iscancelled();
 | |
| }
 | |
| 
 | |
| bool TRecordset::save_as_silk(const char* path)
 | |
| {
 | |
|   TProgind pi(items(), TR("Esportazione in corso..."), true, true);
 | |
| 
 | |
|   ofstream out(path);
 | |
|   out << "ID;PWXL;N;E" << endl;
 | |
| 
 | |
|   // Larghezza colonne
 | |
|   for (unsigned int h = 0; h < columns(); h++)
 | |
|   {
 | |
|     const TRecordset_column_info& ci = column_info(h);
 | |
|     const int w = max(ci._width, ci._name.len());
 | |
|     out << "F;W" << (h+1) << ' ' << (h+1) << ' ' << w << endl;
 | |
|   }
 | |
| 
 | |
|   // Intestazioni colonne
 | |
|   for (unsigned int c = 0; c < columns(); c++)
 | |
|   {
 | |
|     const TRecordset_column_info& ci = column_info(c);
 | |
|     out << "C;Y1;X" << (c+1) << ";K\"" << ci._name << '"' << endl;
 | |
|   }
 | |
| 
 | |
|   TString val;
 | |
|   for (TRecnotype n = 0; n < items(); n++)
 | |
|   {
 | |
|     move_to(n);
 | |
|     pi.addstatus(1);
 | |
|     if (pi.iscancelled())
 | |
|       break;
 | |
|     for (unsigned int c = 0; c < columns(); c++)
 | |
|     {
 | |
|       const TRecordset_column_info& ci = column_info(c);
 | |
|       const bool is_alpha = ci._type == _alfafld || ci._type == _charfld || 
 | |
|                             ci._type == _memofld || ci._type == _datefld;
 | |
| 
 | |
|       out << "C;Y" << (n+2) << ";X" << (c+1) << ";K";
 | |
|       if (is_alpha) out << '"';
 | |
|       get(c).as_string(val);
 | |
|       if (!val.blank())
 | |
|       {
 | |
|         val.rtrim();
 | |
|         val.replace('"', '\'');
 | |
|         out << val;
 | |
|       }
 | |
|       if (is_alpha) out << '"';
 | |
|       out << endl;
 | |
|     }
 | |
|   }
 | |
|   out << "E" << endl;
 | |
|  
 | |
|   return !pi.iscancelled();
 | |
| }
 | |
| 
 | |
| bool TRecordset::save_as_text(const char* path)
 | |
| {
 | |
|   TProgind pi(items(), TR("Esportazione in corso..."), true, true);
 | |
| 
 | |
|   ofstream out(path);
 | |
|   TString val;
 | |
|   for (TRecnotype n = 0; n < items(); n++)
 | |
|   {
 | |
|     move_to(n);
 | |
|     for (unsigned int c = 0; c < columns(); c++)
 | |
|     {
 | |
|       if (c > 0)
 | |
|         out << '\t';
 | |
|       get(c).as_string(val);
 | |
|       if (!val.blank())
 | |
|       {
 | |
|         val.rtrim();
 | |
|         out << val;
 | |
|       }
 | |
|     }
 | |
|     out << endl;
 | |
|     pi.addstatus(1);
 | |
|     if (pi.iscancelled())
 | |
|       break;
 | |
|   }
 | |
|  
 | |
|   return !pi.iscancelled();
 | |
| }
 | |
| 
 | |
| bool TRecordset::save_as_campo(const char* 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 TRecordset_column_info& ci = column_info(c);
 | |
|     if ((c % 8) == 0)
 | |
|       out << endl << "Fields=";
 | |
|     else
 | |
|       out << '|';
 | |
|     out << ci._name;
 | |
|   }
 | |
|   out << endl << endl << "[Data]" << endl;
 | |
| 
 | |
|   TString val;
 | |
|   for (TRecnotype n = 0; n < items(); n++)
 | |
|   {
 | |
|     move_to(n);
 | |
|     for (unsigned int c = 0; c < columns(); c++)
 | |
|     {
 | |
|       if (c > 0)
 | |
|         out << '|';
 | |
|       get(c).as_string(val);
 | |
|       if (!val.blank())
 | |
|       {
 | |
|         val.rtrim();
 | |
| 				val = unesc(val);
 | |
|         out << val;
 | |
|       }
 | |
|     }
 | |
|     out << endl;
 | |
|     pi.addstatus(1);
 | |
|     if (pi.iscancelled())
 | |
|       break;
 | |
|   }
 | |
|   return !pi.iscancelled();
 | |
| }
 | |
| 
 | |
| bool TRecordset::save_as(const char* path, TRecordsetExportFormat fmt, int mode)
 | |
| {
 | |
|   if (fmt == fmt_unknown)
 | |
|   {
 | |
|     TString ext;
 | |
|     xvt_fsys_parse_pathname(path, NULL, NULL, NULL, ext.get_buffer(), NULL);
 | |
|     ext.lower();
 | |
|     if (ext.starts_with("htm"))
 | |
|       fmt = fmt_html; else
 | |
|     if (ext == "xls" || ext == "slk")
 | |
|       fmt = fmt_silk;
 | |
|   }
 | |
|   bool ok = false;
 | |
|   switch (fmt)
 | |
|   {
 | |
|   case fmt_html : ok = save_as_html(path);  break;
 | |
|   case fmt_silk : ok = save_as_silk(path);  break;
 | |
|   case fmt_campo: ok = save_as_campo(path); break;
 | |
|   case fmt_dbf  : ok = save_as_dbf(path, mode);   break;
 | |
|   default       : ok = save_as_text(path);  break;
 | |
|   }
 | |
| 
 | |
|   return ok;
 | |
| }
 | |
| 
 | |
| int TRecordset::find_column(const char* column_name) const
 | |
| {
 | |
| 	int i;
 | |
|   for (i = columns()-1; i >= 0; i--)
 | |
|   {
 | |
|     const TRecordset_column_info& info = column_info(i);
 | |
|     if (info._name == column_name)
 | |
|       break;
 | |
|   }
 | |
|   return i;
 | |
| }
 | |
| 
 | |
| TVariant& TRecordset::get_tmp_var() const
 | |
| {
 | |
|   static TArray _page;      // Variants to be returned by get
 | |
|   static int _next_var = 0; // Index of next variant to be returned
 | |
| 
 | |
|   if (_next_var >= 32)
 | |
|     _next_var = 0;
 | |
|   TVariant* var = (TVariant*)_page.objptr(_next_var);
 | |
|   if (var == NULL)
 | |
|   {
 | |
|     var = new TVariant;
 | |
|     _page.add(var, _next_var);
 | |
|   }
 | |
|   _next_var++;
 | |
|   return *var;
 | |
| }
 | |
| 
 | |
| const TVariant& TRecordset::get(const char* column_name) const
 | |
| {
 | |
|   if (*column_name == '#')
 | |
|     return get_var(column_name);
 | |
| 
 | |
|   char* colon = (char*)strchr(column_name, ':');  //antica porcata
 | |
|   if (colon != NULL)
 | |
|   {
 | |
|     *colon = '\0';
 | |
|     const int i = find_column(column_name);
 | |
|     *colon = ':';
 | |
|     if (i >= 0)
 | |
|     {
 | |
|       const TString& str = get(i).as_string();
 | |
|       TString subfield; subfield << (colon+1) << '=';
 | |
|       int s = str.find(subfield);
 | |
|       if (s == 0 || (s > 0 && str[s-1] < ' '))
 | |
|       {
 | |
|         static TVariant var;
 | |
|         s += subfield.len();
 | |
|         const int e = str.find('\n', s);
 | |
|         var.set(str.sub(s, e));
 | |
|         return var;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
|   else
 | |
|   {
 | |
|     const int i = find_column(column_name);
 | |
|     if (i >= 0)
 | |
|       return get(i);
 | |
|   }
 | |
| 
 | |
|   return NULL_VARIANT;
 | |
| }
 | |
| 
 | |
| const TVariant& TRecordset::get_var(const char* name) const
 | |
| {
 | |
|   // Se mi accorgo che posso e voglio accedere ad un campo del recordset padre
 | |
|   if (_parentset != NULL && strncmp(name, "#PARENT.", 8) == 0) 
 | |
|   {
 | |
|     return _parentset->get(name+8); // Attenzione! E' giusto usare get() e non get_var()
 | |
|   }
 | |
| 
 | |
|   const TVariant* var = (const TVariant*)_var.objptr(name);
 | |
|   return var != NULL ? *var : NULL_VARIANT;
 | |
| }
 | |
| 
 | |
| bool TRecordset::set_var(const char* name, const TVariant& var, bool create)
 | |
| {
 | |
|   bool ok = false;
 | |
|   TVariant* old = (TVariant*)_var.objptr(name);
 | |
|   if (old != NULL)
 | |
|   {
 | |
|     *old = var;
 | |
|     ok = true;
 | |
|   }
 | |
|   else
 | |
|   {
 | |
|     if (create)
 | |
|     {
 | |
|       _var.add(name, var);
 | |
|       _varnames.add(name);
 | |
|       ok = true;
 | |
|     }
 | |
|   }
 | |
|   if (ok)
 | |
|     requery();
 | |
|   return ok;
 | |
| }
 | |
| 
 | |
| bool is_var_separator(char c)
 | |
| {
 | |
|   if (isspace(c))
 | |
|     return true;
 | |
|   return strchr("<=>(,", c) != NULL;
 | |
| }
 | |
| 
 | |
| // Cerca le variabili nel testo SQL:
 | |
| // Una variabile comincia per # ed e' composta da soli caratteri alfanumerici.
 | |
| // Prima del simbolo # e dopo il nome della variabile deve esserci un separatore o blank
 | |
| void TRecordset::find_and_reset_vars()
 | |
| {
 | |
|   _var.destroy();
 | |
|   _varnames.destroy();
 | |
|   
 | |
|   const TString& sql = query_text();
 | |
|   int diesis = sql.find('#'); // cerco il primo #
 | |
|   for ( ; diesis > 0; diesis = sql.find('#', diesis+1)) // Cerco tutti i #
 | |
|   {
 | |
|     if (is_var_separator(sql[diesis-1])) // Controllo che ci sia un separatore prima del #
 | |
|     {
 | |
|       int i = diesis+1;
 | |
|       for ( ; sql[i] && (isalnum(sql[i]) || strchr("@_.#", sql[i]) != NULL); i++);
 | |
|       if (i > diesis+1)
 | |
|       {
 | |
|         const TString& name = sql.sub(diesis, i);
 | |
|         set_var(name, NULL_VARIANT, true);
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| void TRecordset::parsed_text(TString& sql) const
 | |
| {
 | |
|   sql = query_text();
 | |
|   const bool is_isam = sql.starts_with("US");
 | |
|   const bool is_sql = !is_isam;
 | |
|   const char* apici = is_isam ? "\"" : "'";
 | |
| 
 | |
|   const bool vars = ((TSQL_recordset*)this)->ask_variables(false);
 | |
|   if (vars) // Se ci sono variabili faccio le sostituzioni
 | |
|   {
 | |
|     const TString_array& names = variables();
 | |
|     TString s;
 | |
|     FOR_EACH_ARRAY_ROW(names, i, name) // Scandisco tutte le variabili
 | |
|     {
 | |
|       TVariant var = get_var(*name);
 | |
|       int pos = sql.find(*name);
 | |
|       for ( ; pos > 0; pos = sql.find(*name, pos+1))
 | |
|       {
 | |
|         const TString& after = sql.mid(pos+name->len());
 | |
|         sql.cut(pos);
 | |
|         
 | |
|         if (var.type() == _datefld)
 | |
|           s.format("%ld", var.as_date().date2ansi());
 | |
|         else
 | |
|         {
 | |
|           s = var.as_string();
 | |
|           if (is_sql) // Raddoppia gli apici in SQL
 | |
|           {
 | |
|             for (int i = 0; s[i]; i++)
 | |
|             {
 | |
|               if (s[i] == '\'')
 | |
|                 s.insert("'", i++);
 | |
|             }
 | |
|           }
 | |
|         }
 | |
|         if ((var.is_string() && s[0] != *apici && sql.right(1) != apici) || var.is_null())
 | |
|         {
 | |
|           s.insert(apici);
 | |
|           s << apici;
 | |
|         }
 | |
|         sql << s << after;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| bool ask_variable(const char* name, TVariant& var)
 | |
| {
 | |
|   TMask m(TR("Richiesta variabile"), 1, 52, 4);
 | |
|   m.add_static(-1, 0, name, 1, 0);
 | |
|   m.add_string(101, 0, "", 1, 1, 80, "", 50);
 | |
|   m.add_button(DLG_OK, 0, "", -12, -1, 10, 2);
 | |
|   m.add_button(DLG_CANCEL, 0, "", -22, -1, 10, 2);
 | |
|   m.set(101, var.as_string());
 | |
|   const bool ok = m.run() == K_ENTER;
 | |
|   if (ok)
 | |
|   {
 | |
|     const TString& str = m.get(101);
 | |
|     if (is_numeric(str))
 | |
|       var = real(str);
 | |
|     else
 | |
|       var = str;
 | |
|   }
 | |
|   return ok;
 | |
| }
 | |
| 
 | |
| bool TRecordset::ask_variables(bool all)
 | |
| {
 | |
|   const bool ok = variables().items() > 0;
 | |
|   if (ok) // Se ci sono variabili faccio le sostituzioni
 | |
|   {
 | |
|     FOR_EACH_ARRAY_ROW(_varnames, i, name)
 | |
|     {
 | |
|       TVariant var = get_var(*name);
 | |
|       if (var.is_null() || all) 
 | |
|       {
 | |
|         ask_variable(*name, var);
 | |
|         if (var.is_null())
 | |
|           var.set(""); // Mi serve assolutamente un valore!
 | |
|         set_var(*name, var);
 | |
|       }
 | |
|     }
 | |
|   }
 | |
|   return ok;
 | |
| }
 | |
| 
 | |
| TRecordset::TRecordset() : _parentset(NULL)
 | |
| { }
 | |
| 
 | |
| ///////////////////////////////////////////////////////////
 | |
| // Utility
 | |
| ///////////////////////////////////////////////////////////
 | |
| 
 | |
| static void sort_files(TString_array& files)
 | |
| {
 | |
|   TFilename path;
 | |
| 
 | |
|   // Trasforma i path completi in nomi senza estensione
 | |
|   FOR_EACH_ARRAY_ROW(files, i, row)
 | |
|   {
 | |
|     path = *row; 
 | |
|     path = path.name(); 
 | |
|     path.ext("");
 | |
|     path.lower();
 | |
|     *row = path;
 | |
|   }
 | |
|   files.sort();  // Ordina alfabeticamente
 | |
| 
 | |
|   // Rimuove i files doppi proveninenti da Campo e Custom
 | |
|   for (int j = files.last(); j > 0; j--)
 | |
|   {
 | |
|     if (files.row(j) == files.row(j-1))
 | |
|       files.destroy(j);
 | |
|   }
 | |
| }
 | |
| 
 | |
| static bool get_xml_attr(const TString& line, const char* attr, TString& value)
 | |
| {
 | |
|   TString80 str; str << ' ' << attr << "=\"";
 | |
|   const int pos = line.find(str);
 | |
|   if (pos >= 0)
 | |
|   {
 | |
|     const int apicia = line.find('"', pos)+1;
 | |
|     const int apicic = line.find('"', apicia);
 | |
|     if (apicic > apicia)
 | |
|     {
 | |
|       value = line.sub(apicia, apicic);
 | |
|       return true;
 | |
|     }
 | |
|   }
 | |
| 	value.cut(0);
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| static bool get_xml_child(const TString& line, const char* tag, TString& value)
 | |
| {
 | |
|   TString80 str; str << '<' << tag << '>';
 | |
|   const int pos = line.find(str);
 | |
|   if (pos >= 0)
 | |
|   {
 | |
|     const int apicia = line.find('>', pos)+1;
 | |
|     const int apicic = line.find('<', apicia);
 | |
|     if (apicic > apicia)
 | |
|     {
 | |
|       value = line.sub(apicia, apicic);
 | |
|       return true;
 | |
|     }
 | |
|   }
 | |
| 	value.cut(0);
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| bool list_custom_files(const char* ext, const char* classe, TString_array& files)
 | |
| {
 | |
|   TString_array lista;
 | |
|   TFilename path;
 | |
| 
 | |
| 
 | |
|   {
 | |
|     TWait_cursor hourglass;
 | |
| 
 | |
|     TString_array files;
 | |
|     // Leggo i files in custom
 | |
|     if (main_app().has_module(RSAUT))
 | |
|     {
 | |
|       TFilename custom = firm2dir(-1);
 | |
|       custom.add("custom");
 | |
|       if (!custom.exist())
 | |
|         xvt_fsys_mkdir(custom);
 | |
| 
 | |
|       path = custom;
 | |
|       path.add("*");
 | |
|       path.ext(ext);
 | |
| 		}
 | |
|     list_files(path, lista);
 | |
|   }
 | |
|   path = "*"; path.ext(ext);  // Leggo i files in campo
 | |
|   list_files(path, lista);
 | |
|   sort_files(lista);           // Ordino i files e rimuovo i doppioni
 | |
| 
 | |
| 
 | |
|   TString8 acqua;
 | |
|   TString stringona, desc;
 | |
| 
 | |
|   FOR_EACH_ARRAY_ROW(lista, i, row)
 | |
|   {
 | |
|     path = *row; path.ext(ext);
 | |
|     bool ok = path.custom_path();
 | |
|     if (ok)
 | |
|     { 
 | |
|       TScanner scan(path);
 | |
|       stringona.cut(0);
 | |
|       for (int i = 0; i < 3 && scan.good(); i++) // Leggo solo le prime righe
 | |
|         stringona << scan.line();
 | |
|   
 | |
|       get_xml_attr(stringona, "class", acqua);
 | |
|       if (classe && *classe)
 | |
|         ok = acqua == classe;
 | |
| 
 | |
|     
 | |
|       if (ok)
 | |
|       {
 | |
|         get_xml_child(stringona, "description", desc);
 | |
|         TToken_string* riga = new TToken_string;
 | |
|         riga->add(*row);
 | |
|         riga->add(acqua);
 | |
|         riga->add(desc);   
 | |
|         files.add(riga);
 | |
| 
 | |
|       }
 | |
|     }
 | |
|   }
 | |
|   return !files.empty();
 | |
| }
 | |
| 
 | |
| bool select_custom_file(TFilename& path, const char* ext, const char* library)
 | |
| {
 | |
|   TArray_sheet sheet(-1, -1, 78, 20, TR("Selezione"), HR("Nome@8|Classe|Descrizione@50|Custom"));
 | |
|   TString_array& files = sheet.rows_array();
 | |
|   bool ok = list_custom_files(ext, library, files);
 | |
| 
 | |
|   if (ok)
 | |
|   {
 | |
|     TFilename name;
 | |
|     FOR_EACH_ARRAY_ROW(files, i, row)
 | |
|     {
 | |
|       name = row->get(0); 
 | |
|       name.ext(ext);
 | |
|       name.custom_path();
 | |
|       if (name.find("custom") > 0)
 | |
|         row->add("X");
 | |
|     }
 | |
| 
 | |
|     ok = sheet.run() == K_ENTER;
 | |
|     if (ok)
 | |
|     {
 | |
|       path = sheet.row(-1).get(0);
 | |
|       path.ext(ext);
 | |
|       ok = path.custom_path();
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return ok;
 | |
| }
 | |
| 
 | |
| 
 | |
| ///////////////////////////////////////////////////////////
 | |
| // Private interface
 | |
| ///////////////////////////////////////////////////////////
 | |
| #include "../sqlite/sqlite3.h"
 | |
| 
 | |
| class TSQLite : public TObject
 | |
| {
 | |
|   sqlite3* _handle;
 | |
|   TFilename _currdb;
 | |
| 
 | |
| protected:
 | |
|   TVariant& get_sql_value(const TRectype& curr, const RecFieldDes& fd, TVariant& tmp) const;
 | |
| 
 | |
|   void build_curr_path(TFilename& name) const;
 | |
|   void test_path();
 | |
| 
 | |
|   bool bind_record(const TRectype& rec, const RecDes& rd, sqlite3_stmt* pStatement) 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:
 | |
|   sqlite3* open(const char* fname = NULL);
 | |
|   bool exec(const char* sql, sqlite3_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 get_sql_directory(TFilename& name)
 | |
| {
 | |
|   name = firm2dir(-1); 
 | |
|   name.add("sql");
 | |
|   if (!name.exist())
 | |
|     make_dir(name);
 | |
| }
 | |
| 
 | |
| void TSQLite::build_curr_path(TFilename& name) const
 | |
| {
 | |
|   TString16 firm; firm.format("%05ldA.sql", prefix().get_codditta());
 | |
|   get_sql_directory(name);
 | |
|   name.add(firm);
 | |
| }
 | |
| 
 | |
| sqlite3* TSQLite::open(const char* fname)
 | |
| {
 | |
|   close();
 | |
|   _currdb = fname;
 | |
|   int err = sqlite3_open(_currdb, &_handle);
 | |
| 
 | |
|   if (err = SQLITE_CORRUPT)
 | |
|   {
 | |
|     close();
 | |
|     xvt_fsys_removefile(_currdb);
 | |
|     err = sqlite3_open(_currdb, &_handle);
 | |
|   }
 | |
|   
 | |
|   if (err == SQLITE_OK)
 | |
|   {
 | |
|     create_dbf_times();
 | |
|   }
 | |
|   else
 | |
|   {
 | |
|     const char* errmsg = sqlite3_errmsg(_handle); // Stringa di sitema: inutile sqlite3_free(errmsg)
 | |
|     error_box(errmsg);
 | |
| 	}
 | |
| 
 | |
|   return _handle; 
 | |
| }
 | |
| 
 | |
| void TSQLite::test_path()
 | |
| {
 | |
|   TFilename n;
 | |
|   build_curr_path(n);
 | |
|   if (n != _currdb)
 | |
|     open(n);
 | |
| }
 | |
| 
 | |
| bool TSQLite::exec(const char* sql, sqlite3_callback callback, void* jolly, bool show_error)
 | |
| {
 | |
|   if (_handle == NULL)
 | |
|     test_path();
 | |
| 
 | |
|   TWait_cursor hourglass;
 | |
|   char* errmsg = NULL;
 | |
|   const int rc = sqlite3_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);
 | |
|     }
 | |
|     sqlite3_free(errmsg);
 | |
|   }
 | |
|   return rc == SQLITE_OK;
 | |
| }
 | |
| 
 | |
| void TSQLite::close()
 | |
| {
 | |
|   if (_handle != NULL)
 | |
|   {
 | |
|     sqlite3_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 TEXT,time NUMERIC);\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 << "REPLACE INTO " << DBF_TIMES_TABLE << " VALUES("
 | |
|       << '\'' << table << "','" << last << "')"
 | |
|       << "\nWHERE name='" << table << "';";
 | |
|   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 << "\nWHERE name='" << table << "';";
 | |
|   long last = 0;
 | |
|   exec(sql, dbf_time_callback, &last);
 | |
|   return last;
 | |
| }
 | |
| 
 | |
| TVariant& TSQLite::get_sql_value(const TRectype& curr, const RecFieldDes& fd, TVariant& tmp) const
 | |
| {
 | |
|   switch (fd.TypeF)
 | |
|   {
 | |
|   case _realfld    : tmp.set(curr.get_real(fd.Name));  break;
 | |
|   case _intfld     :
 | |
|   case _longfld    :
 | |
|   case _wordfld    :
 | |
|   case _intzerofld :
 | |
|   case _longzerofld: tmp.set(curr.get_long(fd.Name)); break;
 | |
|   case _datefld    : 
 | |
|     {
 | |
|       const TDate date = curr.get_date(fd.Name);
 | |
|       tmp.set(date.date2ansi()); 
 | |
|     }
 | |
|     break;
 | |
|   case _boolfld    : tmp.set(curr.get_bool(fd.Name)); break;
 | |
|   case _memofld:
 | |
|     {
 | |
|       TString memo = curr.get(fd.Name);
 | |
|       memo.replace('\n', char(0xB6)); // Simbolo di paragrafo
 | |
|       tmp.set(memo);
 | |
|     }
 | |
|     break;
 | |
|   default          : tmp.set(curr.get(fd.Name)); 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::bind_record(const TRectype& rec, const RecDes& rd, sqlite3_stmt* pStatement) const
 | |
| {
 | |
|   int rc = SQLITE_OK;
 | |
|   TVariant tmp;
 | |
|   for (int i = 0; i < rd.NFields && rc==SQLITE_OK ; i++)
 | |
|   {
 | |
|     get_sql_value(rec, rd.Fd[i], tmp);
 | |
|     const TString& val = tmp.as_string();
 | |
|     rc = sqlite3_bind_text(pStatement, i+1, val, val.len(), SQLITE_TRANSIENT);
 | |
|   }
 | |
|   return rc == SQLITE_OK;
 | |
| }
 | |
| 
 | |
| bool TSQLite::import(int logicnum)
 | |
| {
 | |
|   const TString& table = logic2table(logicnum);
 | |
| 
 | |
|   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("; 
 | |
|   int i;
 | |
|   for (i = 0; i < rd.NFields; i++)
 | |
|   {
 | |
|     if (i > 0) sql << ',';
 | |
|     sql << rd.Fd[i].Name << ' ';
 | |
|     switch (rd.Fd[i].TypeF)
 | |
|     {
 | |
|     case _alfafld: sql << "TEXT"; break;
 | |
|     case _memofld: sql << "BLOB"; break;
 | |
|     case _datefld: sql << "DATE"; break;
 | |
|     default      : sql << "NUMERIC"; break;
 | |
|     }
 | |
|   }
 | |
|   sql << ");";
 | |
|   if (!exec(sql))
 | |
|     return false;
 | |
| 
 | |
|   TRelation rel(logicnum);
 | |
|   TCursor cur(&rel);
 | |
|   const TRecnotype items = cur.items();
 | |
|   if (items > 0)
 | |
|   {
 | |
|     cur.freeze();
 | |
| 
 | |
|     TString msg;
 | |
|     msg << TR("Importazione tabella") << ' ' << table;
 | |
|     msg << ": " << items << ' ' << TR("righe");
 | |
|     TProgind pi(items, msg, true, true);
 | |
| 
 | |
|     exec("BEGIN"); // Inizio transazione
 | |
| 
 | |
|     // Creo il comando INSERT INTO table VALUES(?,?,?,?,?,?);
 | |
|     sql.cut(0) << "INSERT INTO " << table << " VALUES(";
 | |
|     for (i = 0; i < rd.NFields; i++)
 | |
|     {
 | |
|       if (i != 0) sql << ',';
 | |
|       sql << '?';
 | |
|     }
 | |
|     sql << ");";
 | |
| 
 | |
|     sqlite3_stmt* pStatement = NULL;
 | |
|     int rc = sqlite3_prepare(_handle, sql, sql.len(), &pStatement, NULL);
 | |
|   
 | |
|     const TRectype& curr = rel.curr();
 | |
|     for (cur = 0; cur.pos() < items; ++cur)
 | |
|     {
 | |
|       pi.addstatus(1);
 | |
|       if (pi.iscancelled())
 | |
|         break;
 | |
|       bind_record(curr, rd, pStatement); // Sostituisce i ? coi veri valori
 | |
|       rc = sqlite3_step(pStatement);     // Ritorna sempre 101 (SQLITE_DONE)
 | |
|       rc = sqlite3_reset(pStatement);    // Azzero lo statement per ricominciare
 | |
|     }
 | |
|     rc = sqlite3_finalize(pStatement);
 | |
|     exec("COMMIT");  // Fine transazione
 | |
|   }
 | |
|   
 | |
|   // Creo gli indici DOPO l'importazione per maggiore velocita'
 | |
|   TProgind pi(rd.NKeys, TR("Creazione indici"), false, true);
 | |
|   for (int index = 0; index < rd.NKeys; index++)
 | |
|   {
 | |
|     pi.addstatus(1);
 | |
|     sql.cut(0) << "CREATE INDEX " << table << '_' << (index+1) << " ON "<< table << "\n("; 
 | |
|     const KeyDes& kd = rd.Ky[index];
 | |
|     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 << ");";
 | |
|     exec(sql);
 | |
|   }
 | |
| 
 | |
|   set_dbf_time(table, last);  // Aggiorna ora di ultima modifica
 | |
|  
 | |
|   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 = table2logic(table);
 | |
|     if (logicnum > 0)
 | |
|       import(logicnum);
 | |
|   }
 | |
| 
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| TSQLite::TSQLite() : _handle(NULL)
 | |
| { }
 | |
| 
 | |
| TSQLite::~TSQLite()
 | |
| {
 | |
|   close();
 | |
| }
 | |
| 
 | |
| ///////////////////////////////////////////////////////////
 | |
| // TSQL_recordset
 | |
| ///////////////////////////////////////////////////////////
 | |
| 
 | |
| void TSQL_recordset::reset()
 | |
| {
 | |
|   _items = 0;
 | |
|   _first_row = 0;
 | |
|   _current_row = -1;
 | |
|   _pagesize = 512;
 | |
|   _page.destroy();
 | |
|   _column.destroy();
 | |
| }
 | |
| 
 | |
| int TSQL_recordset::on_get_items(int argc, char** values, char** columns)
 | |
| {
 | |
|   if (_column.items() == 0)
 | |
|   {
 | |
|     for (int i = 0; i < argc; i++)
 | |
|     {
 | |
|       TRecordset_column_info* info = new TRecordset_column_info;
 | |
|       info->_name = columns[i];
 | |
|       info->_width = 1;
 | |
|       info->_type = _alfafld;
 | |
| 
 | |
|       const char* fldtype = columns[argc+i];
 | |
|       if (fldtype != NULL)
 | |
|       {
 | |
|         if (xvt_str_compare_ignoring_case(fldtype, "DATE") == 0)
 | |
|         {
 | |
|           info->_type = _datefld; 
 | |
|           info->_width = 10;
 | |
|         } else
 | |
|         if (xvt_str_compare_ignoring_case(fldtype, "NUMERIC") == 0)
 | |
|         {
 | |
|           info->_type = _realfld;
 | |
|         } else
 | |
|         if (xvt_str_compare_ignoring_case(fldtype, "BLOB") == 0)
 | |
|         {
 | |
|           info->_type = _memofld;
 | |
|           info->_width = 50;
 | |
|         }
 | |
|       }
 | |
|       _column.add(info);
 | |
|     }
 | |
|   }
 | |
|   if (_items < _pagesize)
 | |
|   {
 | |
|     for (int i = 0; i < argc; i++) if (values[i] && *values[i])
 | |
|     {
 | |
|       TRecordset_column_info& info = (TRecordset_column_info&)_column[i];
 | |
|       if (info._type == _alfafld || info._type == _realfld)
 | |
|       {
 | |
|         const int len = strlen(values[i]);
 | |
|         if (len > info._width)
 | |
|           info._width = len;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   _items++;
 | |
|   return SQLITE_OK;
 | |
| }
 | |
| 
 | |
| static int query_get_items(void* jolly, int argc, char** values, char** columns)
 | |
| {
 | |
|   TSQL_recordset* q = (TSQL_recordset*)jolly;
 | |
|   return q->on_get_items(argc, values, columns);
 | |
| }
 | |
| 
 | |
| void TSQL_recordset::requery()
 | |
| {
 | |
|   _items = 0;
 | |
|   _page.destroy();
 | |
| }
 | |
| 
 | |
| TRecnotype TSQL_recordset::items() const
 | |
| {
 | |
|   if (_items == 0)
 | |
|   {
 | |
|     TString sql; parsed_text(sql);
 | |
|     TPerformance_profiler prof("SQL query");
 | |
|     _TheDataBase.exec("PRAGMA show_datatypes = ON;", NULL, NULL);
 | |
|     _TheDataBase.exec(sql, query_get_items, (TSQL_recordset*)this);
 | |
|     _TheDataBase.exec("PRAGMA show_datatypes = OFF;", NULL, NULL);
 | |
|   }
 | |
|   return _items;
 | |
| }
 | |
| 
 | |
| unsigned int TSQL_recordset::columns() const
 | |
| {
 | |
|   if (_column.items() == 0)
 | |
|     items();
 | |
|   return _column.items();
 | |
| }
 | |
|  
 | |
| const TRecordset_column_info& TSQL_recordset::column_info(unsigned int c) const
 | |
| {
 | |
|   return (const TRecordset_column_info&)_column[c];
 | |
| }
 | |
| 
 | |
| // Funzione chiamata per riempire la pagina corrente delle righe della query
 | |
| int TSQL_recordset::on_get_rows(int argc, char** values)
 | |
| {
 | |
|   TArray* a = new TArray;
 | |
|   for (int c = 0; c < argc; c++)
 | |
|   {
 | |
|     TVariant* var = new TVariant;
 | |
|     switch (column_info(c)._type)
 | |
|     {
 | |
|     case _alfafld: 
 | |
|       var->set(values[c]);
 | |
|       break;
 | |
|     case _memofld: 
 | |
|       if (values[c])
 | |
|       {
 | |
|         TFixed_string memo(values[c]);
 | |
|         memo.replace(char(0xB6), '\n');
 | |
|         var->set(memo); 
 | |
|       }
 | |
|       break;
 | |
|     case _datefld: 
 | |
|       var->set(TDate(values[c])); 
 | |
|       break;
 | |
|     default: 
 | |
|       var->set(real(values[c]));
 | |
|       break;
 | |
|     }      
 | |
|     a->add(var, c);
 | |
|   }
 | |
|   _page.add(a);
 | |
|   return SQLITE_OK;
 | |
| }
 | |
| 
 | |
| static int query_get_rows(void* jolly, int argc, char** values, char** /*columns*/)
 | |
| {
 | |
|   TSQL_recordset* rs = (TSQL_recordset*)jolly;
 | |
|   return rs->on_get_rows(argc, values);
 | |
| }
 | |
| 
 | |
| bool TSQL_recordset::move_to(TRecnotype n)
 | |
| {
 | |
|   _current_row = n;
 | |
|   if (n < 0 || n >= items())
 | |
|   {
 | |
|     _page.destroy(); // Forza rilettura la prossiva volta
 | |
|     _first_row = 0;
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   if (n < _first_row || n >= _first_row+_page.items())
 | |
|   {
 | |
|     TString sql; parsed_text(sql);
 | |
|     if (sql.find("LIMIT ") < 0)
 | |
|     {
 | |
|       const int semicolon = sql.rfind(';');
 | |
|       if (semicolon >= 0)
 | |
|         sql.cut(semicolon);
 | |
|       sql.trim();
 | |
|       _page.destroy();
 | |
|       if (n >= _pagesize)
 | |
|         _first_row = n-32; // Prendo qualche riga dalla pagina precedente, per velocizzare il pagina su
 | |
|       else
 | |
|         _first_row = n;
 | |
|       sql << "\nLIMIT " << _pagesize << " OFFSET " << _first_row << ';';
 | |
|     }
 | |
|     _TheDataBase.exec(sql, query_get_rows, this);
 | |
|   }
 | |
| 
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| const TArray* TSQL_recordset::row(TRecnotype n)
 | |
| {
 | |
|   const TArray* a = NULL;
 | |
|   if (move_to(n))
 | |
|     a = (const TArray*)_page.objptr(n-_first_row);
 | |
|   return a;
 | |
| }
 | |
| 
 | |
| const TVariant& TSQL_recordset::get(unsigned int c) const
 | |
| {
 | |
|   const TArray* a = (const TArray*)_page.objptr(_current_row-_first_row);
 | |
|   if (a != NULL)
 | |
|   {
 | |
|     const TVariant* s = (const TVariant*)a->objptr(c);
 | |
|     if (s != NULL)
 | |
|       return *s;
 | |
|   }
 | |
|   return NULL_VARIANT;
 | |
| }
 | |
| 
 | |
| bool TRecordset::save_as_dbf(const char* table, int mode)
 | |
| {
 | |
|   char volume[_MAX_DRIVE];
 | |
|   char dirname[_MAX_PATH];
 | |
|   char name[_MAX_FNAME];
 | |
|   char ext[_MAX_EXT];
 | |
|   xvt_fsys_parse_pathname (table, volume, dirname, name, ext, NULL);
 | |
|   
 | |
|   const int logicnum = table2logic(name);
 | |
|  
 | |
|   if (mode == 0x0)
 | |
|     mode = 0x1;
 | |
|   if (mode & 0x4)
 | |
|   {
 | |
|     if (logicnum >= LF_USER && *dirname == '\0')
 | |
|     {
 | |
|       TSystemisamfile isam(logicnum);
 | |
|       if (isam.zap() != NOERR)
 | |
|         return error_box(TR("Impossibile cancellare il file '%s'"), table);
 | |
|     }
 | |
|     else
 | |
|     {
 | |
|       TFilename n;
 | |
|       n = volume; n.add(dirname); n.add(name); n.ext("*");
 | |
|       TString_array files; list_files(n, files);
 | |
|       FOR_EACH_ARRAY_ROW(files, f, row)
 | |
|         xvt_fsys_removefile(*row);
 | |
|     }
 | |
|     mode = 0x1;
 | |
|   }
 | |
| 
 | |
|   TLocalisamfile* pisam = NULL;
 | |
|   if (logicnum >= LF_USER)
 | |
|   {
 | |
|     if (*dirname)
 | |
|       pisam = new TIsamtempfile(logicnum, table);
 | |
|     else
 | |
|       pisam = new TLocalisamfile(logicnum);
 | |
|   }
 | |
|   if (pisam == NULL)
 | |
|     return error_box("Impossibile creare il file %s", table);
 | |
| 
 | |
|   TLocalisamfile& isam = *pisam;
 | |
| 
 | |
|   TProgind pi(items(), TR("Esportazione in corso..."));
 | |
|   TRectype& rec = isam.curr();
 | |
| 
 | |
|   TString_array names;
 | |
|   int valid = 0;
 | |
|   for (unsigned int j = 0; j < columns(); j++)
 | |
|   {
 | |
|     const TVariant& var = get(j);
 | |
|     const TString& name = column_info(j)._name;
 | |
|     if (rec.exist(name))
 | |
|     {
 | |
|       names.add(name);
 | |
|       valid++;
 | |
|     }
 | |
|     else
 | |
|       names.add(EMPTY_STRING);
 | |
|   }
 | |
| 
 | |
|   bool ok = true;
 | |
|   for (bool go = move_first(); go; go = move_next())
 | |
|   {
 | |
|     pi.addstatus(1);
 | |
|     rec.zero();
 | |
|     FOR_EACH_ARRAY_ROW(names, j, name) if (name->not_empty())
 | |
|       rec.put(*name, get(j).as_string());
 | |
| 
 | |
|     int err = NOERR;
 | |
|     bool to_be_written = true;
 | |
| 
 | |
|     // Devo solo aggiornare parte dei campi?
 | |
|     if ((mode & 0x2) && valid < rec.items())
 | |
|     {
 | |
|       err = isam.read(_isequal, _lock);
 | |
|       if (err != NOERR)
 | |
|         rec.zero();
 | |
|       FOR_EACH_ARRAY_ROW(names, j, name) if (name->not_empty())
 | |
|         rec.put(*name, get(j).as_string());
 | |
|       if (err == NOERR)
 | |
|         to_be_written = isam.rewrite() != NOERR;
 | |
|     }
 | |
|     if (to_be_written)
 | |
|     {
 | |
|       err = (mode & 0x1) ? isam.write() : isam.rewrite();
 | |
|       if (err != NOERR && (mode & 0x3) != 0)
 | |
|         err = (mode & 0x1) ? isam.rewrite() : isam.write();
 | |
|     }
 | |
|     if (err != NOERR)
 | |
|     {
 | |
|       ok = error_box(FR("Errore %d durante la scrittura del record %s"), err, rec.build_key());
 | |
|       break;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return ok;
 | |
| }
 | |
| 
 | |
| 
 | |
| void TSQL_recordset::set(const char* sql) 
 | |
| { 
 | |
|   reset(); 
 | |
|   _sql = sql;
 | |
|   if (_sql.find("SELECT") >= 0 || _sql.find("select") >= 0)
 | |
|   {
 | |
|     _TheDataBase.parse_select_from(_sql);
 | |
|     find_and_reset_vars();
 | |
|   }
 | |
| }
 | |
| 
 | |
| TSQL_recordset::TSQL_recordset(const char* sql) 
 | |
| { 
 | |
|   set(sql); 
 | |
| }
 | |
| 
 | |
| ///////////////////////////////////////////////////////////
 | |
| // TCursor_parser
 | |
| ///////////////////////////////////////////////////////////
 | |
| 
 | |
| class TCursor_parser
 | |
| {
 | |
|   istream& _instr;
 | |
|   TArray& _column;
 | |
| 
 | |
|   TString _pushed;
 | |
|   TString _token;
 | |
|   
 | |
|   TRelation* _relation;
 | |
|   TCursor* _cursor;
 | |
| 
 | |
| protected:
 | |
|   const TString& pop();
 | |
|   const TString& line();
 | |
|   void push();
 | |
|   void add_column_info(const char* table, const TRectype& rec);
 | |
| 
 | |
|   void parse_sortexpr(TToken_string& se);
 | |
|   void parse_filter(TToken_string& filter);
 | |
|   void parse_select(TToken_string& filter);
 | |
|   void parse_region(TRectype& rec);
 | |
|   void parse_join_param(TRelation* rel, const TString& j, int to);
 | |
|   void parse_join();
 | |
|   void parse_sortedjoin();
 | |
| 
 | |
| public:
 | |
|   TRelation* get_relation() { return _relation; }
 | |
|   TCursor* get_cursor() { return _cursor; }
 | |
| 
 | |
|   TCursor_parser(istream& instr, TArray& column);
 | |
| };
 | |
| 
 | |
| const TString& TCursor_parser::pop()
 | |
| {
 | |
|   if (_pushed.not_empty())
 | |
|   {
 | |
|     _token = _pushed;
 | |
|     _pushed.cut(0);
 | |
|   }
 | |
|   else
 | |
|   {
 | |
|     _token.cut(0);  
 | |
|     eatwhite(_instr);
 | |
|     char instring = '\0';
 | |
| 
 | |
|     while (_instr)
 | |
|     {
 | |
|       char c; 
 | |
|       _instr.get(c);
 | |
|       if (c == EOF)
 | |
|         break;
 | |
|       if (instring > ' ') // Sono dentro ad una stringa
 | |
|       {
 | |
|         if (c == '\n')
 | |
|           break;
 | |
|         if (c == instring)
 | |
|           instring = '\0';
 | |
|         _token << c;
 | |
|       }
 | |
|       else
 | |
|       {
 | |
|         if (c == '"' || c == '\'')
 | |
|         {
 | |
|           instring = c;
 | |
|           _token << c;
 | |
|         }
 | |
|         else
 | |
|         {
 | |
|           if (isspace(c))
 | |
|             break;
 | |
|           _token << c;
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|   }
 | |
|   return _token;
 | |
| }
 | |
| 
 | |
| const TString& TCursor_parser::line()
 | |
| {
 | |
|   if (_pushed.not_empty())
 | |
|     return pop();
 | |
|   char* buff = _token.get_buffer(256);
 | |
|   _instr.getline(buff, _token.size());
 | |
|   return _token;
 | |
| }
 | |
| 
 | |
| void TCursor_parser::push()
 | |
| {
 | |
|   CHECK(_pushed.empty(), "Repushing?");
 | |
|   _pushed = _token;
 | |
| }
 | |
| 
 | |
| void TCursor_parser::add_column_info(const char* table, const TRectype& rec)
 | |
| {
 | |
|   for (int i = 0; i < rec.items(); i++)
 | |
|   {
 | |
|     TRecordset_column_info* info = new TRecordset_column_info;
 | |
|     const char* name = rec.fieldname(i);
 | |
|     info->_name << table << '.' << name;
 | |
|     info->_type = rec.type(name);
 | |
|     switch (info->_type)
 | |
|     {
 | |
|     case _datefld: info->_width = 10; break;
 | |
|     case _memofld: info->_width = 50; break;
 | |
|     default      : info->_width = rec.length(name); break;
 | |
|     }
 | |
|     _column.add(info);
 | |
|   }
 | |
| }
 | |
| 
 | |
| void TCursor_parser::parse_sortexpr(TToken_string& se)
 | |
| {
 | |
|   const char sep = se.separator();
 | |
|   se.separator(' ');
 | |
|   _instr.getline(se.get_buffer(), se.size());
 | |
|   se.strip_double_spaces();
 | |
|   se.replace(' ', sep);
 | |
|   se.separator(sep);
 | |
| 
 | |
|   // Trasforma i nomi dei files in numeri se necessario
 | |
|   if (se.find('.') > 0)
 | |
|   {
 | |
|     TToken_string fld(16, '.');
 | |
|     TString16 name;
 | |
|     for (int i = 0; se.get(i, fld); i++)
 | |
|     {
 | |
|       if (!isdigit(fld[0]) && fld.find('.') > 0)
 | |
|       {
 | |
|         fld.get(0, name);
 | |
|         const int num = ::table2logic(name);
 | |
|         if (num != 0)
 | |
|         {
 | |
|           fld.add(num, 0);
 | |
|           se.add(fld, i);
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| void TCursor_parser::parse_filter(TToken_string& filter)
 | |
| {
 | |
|   const TString& str = pop();
 | |
|   while (str.find('=') > 0)
 | |
|   {
 | |
|     filter.add(str);
 | |
|     pop();
 | |
|   }
 | |
|   push();
 | |
| }
 | |
| 
 | |
| void TCursor_parser::parse_select(TToken_string& filter)
 | |
| {
 | |
|   filter = line();
 | |
|   filter.trim();
 | |
| }
 | |
| 
 | |
| 
 | |
| void TCursor_parser::parse_region(TRectype& rec)
 | |
| {
 | |
|   TString16 field;
 | |
|   TString80 value;
 | |
|   while (true)
 | |
|   {
 | |
|     const TString& ass = pop();
 | |
|     const int equal = ass.find('=');
 | |
|     if (equal > 0)
 | |
|     {
 | |
|       field = ass.left(equal);
 | |
|       value = ass.mid(equal+1);
 | |
|       if (value[0] == '"' || value[0] == '\'')
 | |
|       {
 | |
|         value.rtrim(1);
 | |
|         value.ltrim(1);
 | |
|       }
 | |
|       rec.put(field, value);       
 | |
|     }
 | |
|     else
 | |
|       break;
 | |
|   }
 | |
|   push();
 | |
| }
 | |
| 
 | |
| 
 | |
| void TCursor_parser::parse_join_param(TRelation* rel, const TString& j, int to)
 | |
| {
 | |
|   int key = 1;
 | |
|   const TString& tok = pop();
 | |
|   if (tok.starts_with("KE"))
 | |
|   {
 | |
|     pop();
 | |
|     key = atoi(tok);
 | |
|   }
 | |
|   else 
 | |
|     push();
 | |
| 
 | |
|   int alias = 0;
 | |
|   pop();
 | |
|   if (tok.starts_with("AL"))
 | |
|   {
 | |
|     pop();
 | |
|     alias = atoi(tok);
 | |
|   }
 | |
|   else 
 | |
|     push();
 | |
| 
 | |
|   TToken_string exp(80);
 | |
|   pop();
 | |
|   if (tok == "INTO")
 | |
|   {
 | |
|     parse_filter(exp);
 | |
|   }
 | |
|   if (exp.empty())
 | |
|     yesnofatal_box("JOIN senza espressioni INTO");
 | |
| 
 | |
|   const int logicnum = table2logic(j);
 | |
|   if (logicnum != LF_TAB && logicnum != LF_TABCOM)
 | |
|     rel->add(logicnum, exp, key, to, alias);   // join file
 | |
|   else
 | |
|     rel->add(j, exp, key, to, alias);         // join table
 | |
| 
 | |
|   TString16 tabname;
 | |
|   if (alias > 0)
 | |
|     tabname << alias << '@'; 
 | |
|   else
 | |
|     tabname = j;
 | |
|   const TRectype& rec = rel->curr(logicnum);
 | |
|   add_column_info(tabname, rec);    
 | |
| }
 | |
| 
 | |
| void TCursor_parser::parse_join()
 | |
| {
 | |
|   const TString j = pop();           // File or table
 | |
| 
 | |
|   int to = 0;
 | |
|   const TString& tok = pop();
 | |
|   if (tok == "TO")         // TO keyword
 | |
|   {
 | |
|     pop();
 | |
|     to = table2logic(tok);
 | |
|   }
 | |
|   else 
 | |
|     push();
 | |
|   
 | |
|   parse_join_param(_relation, j, to);
 | |
| }
 | |
| 
 | |
| void TCursor_parser::parse_sortedjoin()
 | |
| {
 | |
|   TToken_string filter,sortexp;
 | |
|   const TString j = pop();           // File or table
 | |
|   const TString& tok = pop(); 
 | |
|   if (tok == "BY" )
 | |
|   {
 | |
|     parse_sortexpr(sortexp);
 | |
|   } 
 | |
|   else 
 | |
|     push();
 | |
|  
 | |
|   pop();
 | |
|   if (tok.starts_with("FI") || tok.starts_with("SE")) 
 | |
|   {
 | |
|     parse_select(filter);
 | |
|   } 
 | |
|   else 
 | |
|     push();
 | |
|   
 | |
|   TRelation* sortrel = new TRelation(table2logic(j)); 
 | |
|   while (true)
 | |
|   {
 | |
|     pop();
 | |
|     if (tok.empty() || tok.starts_with("JO"))
 | |
|     {
 | |
|       push();
 | |
|       break;
 | |
|     }
 | |
| 
 | |
|     if (tok.starts_with("US"))         // USING keyword
 | |
|     {
 | |
|       const TString subj = pop();      // File or table
 | |
|       parse_join_param(sortrel, subj, table2logic(j));
 | |
|     } 
 | |
|   } 
 | |
| 
 | |
|   int to = 0;
 | |
|   pop();
 | |
|   if (tok == "TO")         // TO keyword
 | |
|   {
 | |
|     pop();
 | |
|     to = table2logic(tok);
 | |
|   }
 | |
|   else 
 | |
|     push();
 | |
| 
 | |
|   int key = 1;
 | |
|   pop();
 | |
|   if (tok.starts_with("KE"))
 | |
|   {
 | |
|     pop();
 | |
|     key = atoi(tok);
 | |
|   }
 | |
|   else 
 | |
|     push();
 | |
| 
 | |
|   int alias = 0;
 | |
|   pop();
 | |
|   if (tok.starts_with("AL"))
 | |
|   {
 | |
|     pop();
 | |
|     alias = atoi(tok);
 | |
|   }
 | |
|   else 
 | |
|     push();
 | |
| 
 | |
|   TToken_string exp(80);
 | |
|   if (pop() == "INTO")
 | |
|   {
 | |
|     pop();
 | |
|     while (tok.find('=') > 0)
 | |
|     {
 | |
|       exp.add(tok);
 | |
|       pop();
 | |
|     }
 | |
|   }
 | |
|   push();
 | |
| 
 | |
|   TSortedfile *sf= new TSortedfile(atoi(j),sortrel,sortexp,filter,key);
 | |
|   _relation->add((TLocalisamfile *)sf, exp, key, to, alias, false);         // join table
 | |
| 
 | |
|   TString16 tabname = j;
 | |
|   if (alias > 0)
 | |
|     tabname.cut(0) << alias << '@'; 
 | |
|   add_column_info(tabname, sf->curr());    
 | |
| }
 | |
| 
 | |
| TCursor_parser::TCursor_parser(istream& instr, TArray& col) 
 | |
|                 : _instr(instr), _column(col), _relation(NULL), _cursor(NULL)
 | |
| {
 | |
|   _column.destroy();
 | |
|   const TString& tok = pop();
 | |
|   if (!tok.starts_with("US"))
 | |
|     push();
 | |
| 
 | |
|   pop();
 | |
|   if (tok.blank())
 | |
|     return;
 | |
| 
 | |
|   int logicnum = table2logic(tok);
 | |
|   if (logicnum == LF_MAG && tok == "MAG")  // Faccio prevalere la tabella MAG sul file MAG
 | |
|     logicnum = LF_TAB;
 | |
| 
 | |
|   if (logicnum == LF_TAB || logicnum == LF_TABCOM)
 | |
|     _relation = new TRelation(tok);
 | |
|   else
 | |
|     _relation = new TRelation(logicnum);
 | |
|   add_column_info(tok, _relation->curr());
 | |
| 
 | |
|   int key = 1;  // key number
 | |
|   pop();
 | |
|   if (tok.starts_with("KE"))
 | |
|   {
 | |
|     pop();
 | |
|     key = atoi(tok);
 | |
|   }
 | |
|   else
 | |
|     push();
 | |
| 
 | |
|   pop();
 | |
|   
 | |
| 	TToken_string filter;
 | |
| 
 | |
| 	if (tok.starts_with("FI") || tok.starts_with("SE"))
 | |
|     parse_select(filter);
 | |
|   else
 | |
|     push();
 | |
| 
 | |
|   pop();
 | |
|   if (tok.starts_with("BY"))  // "sort BY": user-defined sort
 | |
|   { 
 | |
|     TToken_string ordexpr(256);
 | |
|     parse_sortexpr(ordexpr);
 | |
|     _cursor = new TSorted_cursor(_relation, ordexpr, "", key);
 | |
|   }
 | |
|   else
 | |
|     push();
 | |
| 
 | |
| 
 | |
|   if (_cursor == NULL)
 | |
|     _cursor = new TCursor(_relation, "", key);
 | |
| 
 | |
|   TRectype rec_start(_relation->curr());
 | |
|   TRectype rec_stop(rec_start);
 | |
| 
 | |
|   while (true)
 | |
|   {
 | |
|     pop();
 | |
|     if (tok.starts_with("FR"))
 | |
|       parse_region(rec_start); else
 | |
|     if (tok.starts_with("TO"))
 | |
|       parse_region(rec_stop); else
 | |
|     if (tok.starts_with("JO"))
 | |
|       parse_join(); else
 | |
|     if (tok.starts_with("SO"))
 | |
|       parse_sortedjoin(); 
 | |
|     else
 | |
|       break;
 | |
|   }
 | |
|   push(); 
 | |
| 
 | |
|   if (!rec_start.empty() || !rec_stop.empty())
 | |
|     _cursor->setregion(rec_start, rec_stop);
 | |
| 	if (!filter.empty())
 | |
| 		_cursor->setfilter(filter);
 | |
|   if (_relation->items() == 0) // Non ci sono anche tabelle collegate
 | |
|   {
 | |
|     FOR_EACH_ARRAY_ITEM(_column, i, obj)
 | |
|     {
 | |
|       TRecordset_column_info* info = (TRecordset_column_info*)obj;
 | |
|       const int arrow = info->_name.find('.');
 | |
|       if (arrow > 0)
 | |
|         info->_name = info->_name.mid(arrow+1);
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| ///////////////////////////////////////////////////////////
 | |
| // TISAM_recordset
 | |
| ///////////////////////////////////////////////////////////
 | |
| 
 | |
| const TVariant& TISAM_recordset::get_field(int logic, const char* fldname) const
 | |
| {
 | |
|   const int idx = relation()->log2ind(logic);
 | |
|   if (idx < 0)
 | |
|     return NULL_VARIANT;
 | |
| 
 | |
|   TString80 name = fldname;
 | |
|   TString16 subfield;
 | |
|   int from = 1, to = 0;
 | |
| 
 | |
|   const int open_bracket = name.find('[');
 | |
|   if (open_bracket > 0)
 | |
|   {
 | |
|     sscanf((const char*)name + open_bracket, "[%d,%d]", &from, &to);
 | |
|     name.cut(open_bracket);
 | |
|   }
 | |
| 
 | |
|   const int colon = name.find(':');
 | |
| 
 | |
|   if (colon > 0)
 | |
|   {
 | |
|     subfield = name.mid(colon+1);
 | |
|     name.cut(colon);
 | |
|   }
 | |
| 
 | |
|   const TRectype& rec = relation()->file(idx).curr();
 | |
|   const TFieldtypes ft = rec.type(name);
 | |
| 
 | |
|   if (ft == _nullfld)
 | |
|   {
 | |
|     if (logic == LF_DOC) // Proviamo la magia
 | |
|     {
 | |
|       subfield = name;
 | |
|       name = "G1";
 | |
|     }
 | |
|     else
 | |
|       return NULL_VARIANT;
 | |
|   }
 | |
| 
 | |
|   TVariant& var = get_tmp_var();
 | |
|   switch (ft)
 | |
|   {
 | |
|   case _datefld: var.set(rec.get_date(name)); break;
 | |
|   case _realfld: var.set(rec.get_real(name)); break;
 | |
|   case _intfld : 
 | |
|   case _longfld:
 | |
|   case _wordfld: var.set(rec.get_long(name)); break;
 | |
|   default      : var.set(rec.get(name)); break;
 | |
|   }
 | |
| 
 | |
|   if (subfield.not_empty())
 | |
|   {
 | |
|     subfield << '=';
 | |
|     const TString& str = var.as_string();
 | |
|     int s = str.find(subfield);
 | |
|     if (s == 0 || (s > 0 && str[s-1] < ' '))
 | |
|     {
 | |
|       s += subfield.len();
 | |
|       const int e = str.find('\n', s);
 | |
|       var.set(str.sub(s, e));
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   if (to >= from)
 | |
|     var.set(var.as_string().sub(from-1, to));
 | |
| 
 | |
|   return var;
 | |
| }
 | |
| 
 | |
| const TVariant& TISAM_recordset::get(size_t c) const
 | |
| {
 | |
|   const TRecordset_column_info* info = (const TRecordset_column_info*)_column.objptr(c);
 | |
|   if (info != NULL)
 | |
|   {
 | |
|     int logic = 0;
 | |
|     const char* field = info->_name;
 | |
|     const int dot = info->_name.find('.');
 | |
|     if (dot > 0)
 | |
|     {
 | |
|       logic = table2logic(info->_name.left(dot));
 | |
|       field += dot+1;
 | |
|     }
 | |
|     return get_field(logic, field);
 | |
|   }
 | |
|   return NULL_VARIANT;
 | |
| }
 | |
| 
 | |
| const TVariant& TISAM_recordset::get(const char* name) const
 | |
| {
 | |
|   if (*name == '#')
 | |
|     return get_var(name);
 | |
| 
 | |
|   const TFixed_string fldname(name);
 | |
|   
 | |
|   int table_end = fldname.find('.');
 | |
|   int field_start = table_end+1;
 | |
|   if (table_end < 0)
 | |
|   {
 | |
|     table_end = fldname.find('-');
 | |
|     if (table_end > 0)
 | |
|       field_start = table_end+2;
 | |
|   }
 | |
|   int logic = 0;
 | |
|   const char* field = name;
 | |
|   if (table_end > 0)
 | |
|   {
 | |
|     const TString& table = fldname.left(table_end);
 | |
|     logic = table2logic(table);
 | |
|     field += field_start;
 | |
|   }
 | |
|   return get_field(logic, field);
 | |
| }
 | |
| 
 | |
| const TRecordset_column_info& TISAM_recordset::column_info(size_t i) const
 | |
| {
 | |
|   return (const TRecordset_column_info&)_column[i];
 | |
| }
 | |
| 
 | |
| TCursor* TISAM_recordset::cursor() const
 | |
| {
 | |
|   if (_cursor == NULL && !_use.blank())
 | |
|   {
 | |
|     TString use; parsed_text(use);
 | |
|     TPerformance_profiler prof("ISAM query");
 | |
|     TISAM_recordset* my = (TISAM_recordset*)this;
 | |
| #ifdef LINUX
 | |
| 		string s(use.get_buffer());
 | |
|     istringstream instr(s);
 | |
| #else
 | |
|     istrstream instr(use.get_buffer(), use.len()+1);  //"barata" x aggiungere il carattere finale
 | |
| #endif
 | |
|     TCursor_parser parser(instr, my->_column);
 | |
| 
 | |
|     my->_relation = parser.get_relation();
 | |
|     my->_cursor = parser.get_cursor();
 | |
| 
 | |
|     if (_cursor != NULL)
 | |
|     {
 | |
|       set_custom_filter(*_cursor);
 | |
|       const TRecnotype items = _cursor->items();
 | |
| 
 | |
|       _cursor->freeze();
 | |
| 			if (items > 0)
 | |
| 				*_cursor = 0L;
 | |
|     }
 | |
|   }
 | |
|   return _cursor;
 | |
| }
 | |
| 
 | |
| TRelation* TISAM_recordset::relation() const
 | |
| {
 | |
|   cursor();
 | |
|   return _relation;
 | |
| }
 | |
| 
 | |
| TRecnotype TISAM_recordset::current_row() const
 | |
| {
 | |
|   TCursor* c = cursor();
 | |
|   return c != NULL ? c->pos() : -1;
 | |
| }
 | |
| 
 | |
| TRecnotype TISAM_recordset::items() const 
 | |
| {
 | |
|   TCursor* c = cursor();
 | |
|   return c != NULL ? c->items() : 0;
 | |
| }
 | |
| 
 | |
| unsigned int TISAM_recordset::columns() const 
 | |
| { 
 | |
|   cursor();
 | |
|   return _column.items(); 
 | |
| }
 | |
| 
 | |
| 
 | |
| bool TISAM_recordset::move_to(TRecnotype pos)
 | |
| {
 | |
|   TCursor* c = cursor();
 | |
|   bool ok = c != NULL && pos >= 0;
 | |
|   if (ok)
 | |
|   {
 | |
|     *c = pos;
 | |
|     ok = pos >= 0 && pos < c->items();
 | |
|   }
 | |
|   return ok;
 | |
| }
 | |
| 
 | |
| void TISAM_recordset::reset()
 | |
| {
 | |
|   _column.destroy();
 | |
|   if (_relation != NULL)
 | |
|   {
 | |
|     delete _relation; 
 | |
|     _relation = NULL;
 | |
|   }
 | |
|   if (_cursor != NULL)
 | |
|   {
 | |
|     delete _cursor; 
 | |
|     _cursor = NULL;
 | |
|   }
 | |
| }
 | |
| 
 | |
| void TISAM_recordset::requery()
 | |
| {
 | |
|   if (_cursor != NULL)
 | |
|   {
 | |
|     delete _cursor; 
 | |
|     _cursor = NULL;
 | |
|   }
 | |
| }
 | |
| 
 | |
| void TISAM_recordset::set(const char* use)
 | |
| {
 | |
|   reset();
 | |
|   _use = use;
 | |
|   find_and_reset_vars(); 
 | |
| }
 | |
| 
 | |
| TISAM_recordset::TISAM_recordset(const char* use) 
 | |
|                : _relation(NULL), _cursor(NULL)
 | |
| { 
 | |
|   set(use); 
 | |
| }
 | |
| 
 | |
| TISAM_recordset::~TISAM_recordset()
 | |
| {
 | |
|   reset();
 | |
| }
 | |
| 
 | |
| ///////////////////////////////////////////////////////////
 | |
| // TRecordset_sheet
 | |
| ///////////////////////////////////////////////////////////
 | |
| 
 | |
| void TRecordset_sheet::get_row(long r, TToken_string& row)
 | |
| {
 | |
|   row.separator('\t');
 | |
|   row.cut(0);
 | |
|   if (_query.move_to(r))
 | |
|   {
 | |
|     TString str;
 | |
|     for (unsigned int c = 0; c < _query.columns(); c++)
 | |
|     {
 | |
|       _query.get(c).as_string(str);
 | |
|       row.add(str);
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| long TRecordset_sheet::get_items() const
 | |
| {
 | |
|   return _query.items();
 | |
| }
 | |
| 
 | |
| TRecordset_sheet::TRecordset_sheet(TRecordset& query) 
 | |
|                 : TSheet(-1, -1, -2, -4, "Query", query.sheet_head()), _query(query)
 | |
| {
 | |
| } |