Files correlati : Ricompilazione Demo : [ ] Commento : Riportata la versione 3.1 patch 650 git-svn-id: svn://10.65.10.50/trunk@14148 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();
 | 
						|
        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 = strchr(column_name, ':');
 | 
						|
  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, 80, 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 << "');";
 | 
						|
  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;
 | 
						|
}
 | 
						|
 | 
						|
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);
 | 
						|
    if (_instr.eof())
 | 
						|
      return _token;
 | 
						|
  
 | 
						|
    char instring = '\0';
 | 
						|
    while (true)
 | 
						|
    {
 | 
						|
      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(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(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(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());
 | 
						|
#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 < 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)
 | 
						|
{
 | 
						|
} |