Patch level : 4.0
Files correlati : Ricompilazione Demo : [ ] Commento : Eseguito splitting tra sqlrecordset ed isamrecordset. Migliorato supporto per immagini e bottoni grigi git-svn-id: svn://10.65.10.50/trunk@15009 c028cbd2-c16b-5b4b-a496-9718f37d4682
This commit is contained in:
		
							parent
							
								
									dcf8d84484
								
							
						
					
					
						commit
						32088954c5
					
				@ -111,7 +111,7 @@ short TPicture_array::add(const char * n)
 | 
				
			|||||||
	_enabled.add(i, id);
 | 
						_enabled.add(i, id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  TImage* d = new TImage(*i);
 | 
					  TImage* d = new TImage(*i);
 | 
				
			||||||
  d->fade_to_gray(COLOR_GRAY);
 | 
					  d->fade_to_gray(true);
 | 
				
			||||||
  _disabled.add(d, id);
 | 
					  _disabled.add(d, id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return id;
 | 
					  return id;
 | 
				
			||||||
@ -139,7 +139,7 @@ bool TPicture_array::add(short id)
 | 
				
			|||||||
      _enabled.add(i, id);
 | 
					      _enabled.add(i, id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      TImage* d = new TImage(*i);
 | 
					      TImage* d = new TImage(*i);
 | 
				
			||||||
      d->fade_to_gray(COLOR_GRAY);
 | 
					      d->fade_to_gray(true);
 | 
				
			||||||
      _disabled.add(d, id);
 | 
					      _disabled.add(d, id);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		else
 | 
							else
 | 
				
			||||||
 | 
				
			|||||||
@ -99,7 +99,7 @@ bool TGolem_mask::file_handler(TMask_field& f, KEY k)
 | 
				
			|||||||
    if (!f.empty())
 | 
					    if (!f.empty())
 | 
				
			||||||
    { 
 | 
					    { 
 | 
				
			||||||
      const TFilename n = f.get();
 | 
					      const TFilename n = f.get();
 | 
				
			||||||
      xvt_fsys_convert_str_to_dir((char*)(const char*)n.path(), &fs.dir);
 | 
					      xvt_fsys_convert_str_to_dir(n.path(), &fs.dir);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    else
 | 
					    else
 | 
				
			||||||
      xvt_fsys_convert_str_to_dir(".", &fs.dir);
 | 
					      xvt_fsys_convert_str_to_dir(".", &fs.dir);
 | 
				
			||||||
@ -1177,7 +1177,7 @@ int TMail_messages::get(const char* senderFilter,
 | 
				
			|||||||
        const MapiRecipDesc* pSender = pMessage->lpOriginator;
 | 
					        const MapiRecipDesc* pSender = pMessage->lpOriginator;
 | 
				
			||||||
        sender = pSender->lpszName;
 | 
					        sender = pSender->lpszName;
 | 
				
			||||||
        if (senderFilter && *senderFilter)
 | 
					        if (senderFilter && *senderFilter)
 | 
				
			||||||
          should_add = stricmp(senderFilter, sender) == 0;
 | 
					          should_add = xvt_str_compare_ignoring_case(senderFilter, sender) == 0;
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      if (should_add)
 | 
					      if (should_add)
 | 
				
			||||||
 | 
				
			|||||||
@ -243,14 +243,10 @@ void TImage::convert_to_default_colors()
 | 
				
			|||||||
				const COLOR c = xvt_image_get_clut(_image, index) & 0x00FFFFFF;
 | 
									const COLOR c = xvt_image_get_clut(_image, index) & 0x00FFFFFF;
 | 
				
			||||||
				switch (c)
 | 
									switch (c)
 | 
				
			||||||
				{
 | 
									{
 | 
				
			||||||
				case COLOR_DKCYAN & 0x00FFFFFF:
 | 
									case COLOR_DKCYAN & 0x00FFFFFF:	xvt_image_set_clut(_image, index, MASK_BACK_COLOR); break;
 | 
				
			||||||
					xvt_image_set_clut(_image, index, MASK_BACK_COLOR); break;
 | 
									case COLOR_CYAN   & 0x00FFFFFF:	xvt_image_set_clut(_image, index, MASK_LIGHT_COLOR); break;
 | 
				
			||||||
				case COLOR_CYAN & 0x00FFFFFF:
 | 
									case COLOR_GRAY   & 0x00FFFFFF: xvt_image_set_clut(_image, index, MASK_DARK_COLOR); break;
 | 
				
			||||||
					xvt_image_set_clut(_image, index, MASK_LIGHT_COLOR); break;
 | 
									default: break;  
 | 
				
			||||||
				case COLOR_GRAY & 0x00FFFFFF:
 | 
					 | 
				
			||||||
					xvt_image_set_clut(_image, index, MASK_DARK_COLOR); break;
 | 
					 | 
				
			||||||
				default:
 | 
					 | 
				
			||||||
					break;  
 | 
					 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
@ -262,14 +258,10 @@ void TImage::convert_to_default_colors()
 | 
				
			|||||||
				const COLOR c = get_pixel(x, y) & 0x00FFFFFF;
 | 
									const COLOR c = get_pixel(x, y) & 0x00FFFFFF;
 | 
				
			||||||
				switch (c)
 | 
									switch (c)
 | 
				
			||||||
				{
 | 
									{
 | 
				
			||||||
				case COLOR_DKCYAN & 0x00FFFFFF:
 | 
									case COLOR_DKCYAN & 0x00FFFFFF: set_pixel(x, y, MASK_BACK_COLOR); break;
 | 
				
			||||||
					set_pixel(x, y, MASK_BACK_COLOR); break;
 | 
									case COLOR_CYAN & 0x00FFFFFF  :	set_pixel(x, y, MASK_LIGHT_COLOR); break;
 | 
				
			||||||
				case COLOR_CYAN & 0x00FFFFFF:
 | 
									case COLOR_GRAY & 0x00FFFFFF  :	set_pixel(x, y, MASK_DARK_COLOR); break;
 | 
				
			||||||
					set_pixel(x, y, MASK_LIGHT_COLOR); break;
 | 
									default: break;  
 | 
				
			||||||
				case COLOR_GRAY & 0x00FFFFFF:
 | 
					 | 
				
			||||||
					set_pixel(x, y, MASK_DARK_COLOR); break;
 | 
					 | 
				
			||||||
				default:
 | 
					 | 
				
			||||||
					break;  
 | 
					 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
@ -278,7 +270,6 @@ void TImage::convert_to_default_colors()
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// @mfunc Setta i colori dell'immagine  in modo da renderla trasparente
 | 
					// @mfunc Setta i colori dell'immagine  in modo da renderla trasparente
 | 
				
			||||||
void TImage::convert_transparent_color(COLOR transparent)
 | 
					void TImage::convert_transparent_color(COLOR transparent)
 | 
				
			||||||
 | 
					 | 
				
			||||||
  // @comm Legge nell'immagine i pixel uguali a quello in alto a sinistra e li setta 
 | 
					  // @comm Legge nell'immagine i pixel uguali a quello in alto a sinistra e li setta 
 | 
				
			||||||
  // uguali allo sfondo delle maschere
 | 
					  // uguali allo sfondo delle maschere
 | 
				
			||||||
{                               
 | 
					{                               
 | 
				
			||||||
@ -310,7 +301,26 @@ void TImage::convert_transparent_color(COLOR transparent)
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void TImage::fade_to_gray(int gray)
 | 
					inline COLOR btncolor2btngray(COLOR color, bool use_btn)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  static COLOR last_color = -1, last_gray = -1;
 | 
				
			||||||
 | 
					  if (color != last_color)
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    last_gray = grayed_color(last_color = color);
 | 
				
			||||||
 | 
					    if (use_btn)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      // Prendo una sola componente del grigio: tanto sono identiche 
 | 
				
			||||||
 | 
					      const int g = XVT_COLOR_GET_RED(last_gray); 
 | 
				
			||||||
 | 
					      if (g > 128) // Chiaro
 | 
				
			||||||
 | 
					        last_gray = blend_colors(BTN_LIGHT_COLOR, BTN_BACK_COLOR, (g-128.0)/128.0);
 | 
				
			||||||
 | 
					      else         // Scuro
 | 
				
			||||||
 | 
					        last_gray = blend_colors(BTN_BACK_COLOR, BTN_DARK_COLOR, g/128.0);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  return last_gray;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void TImage::fade_to_gray(bool use_btn_colors)
 | 
				
			||||||
  // @comm Legge nell'immagine i pixel diversi da quello in alto a sinistra e li setta 
 | 
					  // @comm Legge nell'immagine i pixel diversi da quello in alto a sinistra e li setta 
 | 
				
			||||||
  // in grigio
 | 
					  // in grigio
 | 
				
			||||||
{                               
 | 
					{                               
 | 
				
			||||||
@ -318,21 +328,14 @@ void TImage::fade_to_gray(int gray)
 | 
				
			|||||||
		return;  // Null image
 | 
							return;  // Null image
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const COLOR trans = get_pixel(0,0) & 0x00FFFFFF;
 | 
						const COLOR trans = get_pixel(0,0) & 0x00FFFFFF;
 | 
				
			||||||
 | 
					  btncolor2btngray(trans, use_btn_colors);  // Reset color conversion
 | 
				
			||||||
	if (xvt_image_get_format(_image) == XVT_IMAGE_CL8)
 | 
						if (xvt_image_get_format(_image) == XVT_IMAGE_CL8)
 | 
				
			||||||
	{                  
 | 
						{                  
 | 
				
			||||||
		for (int index = xvt_image_get_ncolors(_image)-1; index >=0; index--)
 | 
							for (int index = xvt_image_get_ncolors(_image)-1; index >=0; index--)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
      COLOR pixie = xvt_image_get_clut(_image, index) & 0x00FFFFFF; 
 | 
					      COLOR pixie = xvt_image_get_clut(_image, index) & 0x00FFFFFF; 
 | 
				
			||||||
			if (pixie != trans)
 | 
								if (pixie != trans)
 | 
				
			||||||
			{
 | 
									xvt_image_set_clut(_image, index, btncolor2btngray(pixie, use_btn_colors));
 | 
				
			||||||
        pixie = grayed_color(pixie);
 | 
					 | 
				
			||||||
        if (gray >= 0)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
          const int k = (XVT_COLOR_GET_RED(pixie) + gray) / 2;  
 | 
					 | 
				
			||||||
          pixie = MAKE_COLOR(k,k,k);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
				xvt_image_set_clut(_image, index, pixie);
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
	} 
 | 
						} 
 | 
				
			||||||
	else
 | 
						else
 | 
				
			||||||
@ -342,15 +345,7 @@ void TImage::fade_to_gray(int gray)
 | 
				
			|||||||
		{     
 | 
							{     
 | 
				
			||||||
			COLOR pixie = get_pixel(x, y) & 0x00FFFFFF;
 | 
								COLOR pixie = get_pixel(x, y) & 0x00FFFFFF;
 | 
				
			||||||
			if (pixie != trans)
 | 
								if (pixie != trans)
 | 
				
			||||||
      {
 | 
									set_pixel(x, y, btncolor2btngray(pixie, use_btn_colors));
 | 
				
			||||||
        pixie = grayed_color(pixie);
 | 
					 | 
				
			||||||
        if (gray >= 0)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
          const int k = (XVT_COLOR_GET_RED(pixie) + gray) / 2;  
 | 
					 | 
				
			||||||
          pixie = MAKE_COLOR(k,k,k);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
				set_pixel(x, y, pixie);
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -86,7 +86,7 @@ public:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  // Trasforma l'immagine in toni di grigio.  
 | 
					  // Trasforma l'immagine in toni di grigio.  
 | 
				
			||||||
  // Se gray>=0 sfuma ulteriormente verso il tono specificato
 | 
					  // Se gray>=0 sfuma ulteriormente verso il tono specificato
 | 
				
			||||||
  void fade_to_gray(int gray = -1);
 | 
					  void fade_to_gray(bool use_btn_colors);
 | 
				
			||||||
  
 | 
					  
 | 
				
			||||||
  // @cmember Setta l'<p n>.esime entry della paletta al colore <p c>
 | 
					  // @cmember Setta l'<p n>.esime entry della paletta al colore <p c>
 | 
				
			||||||
  void set_clut(byte n, COLOR c);
 | 
					  void set_clut(byte n, COLOR c);
 | 
				
			||||||
 | 
				
			|||||||
@ -8,11 +8,9 @@
 | 
				
			|||||||
#include <execp.h>
 | 
					#include <execp.h>
 | 
				
			||||||
#include <expr.h>
 | 
					#include <expr.h>
 | 
				
			||||||
#include <msksheet.h>
 | 
					#include <msksheet.h>
 | 
				
			||||||
//#include <prefix.h> 
 | 
					 | 
				
			||||||
#include <recarray.h>
 | 
					#include <recarray.h>
 | 
				
			||||||
#include <recset.h>
 | 
					#include <recset.h>
 | 
				
			||||||
#include <relapp.h>
 | 
					#include <relapp.h>
 | 
				
			||||||
//#include <relation.h>
 | 
					 | 
				
			||||||
#include <sheet.h>
 | 
					#include <sheet.h>
 | 
				
			||||||
#include <tabutil.h>
 | 
					#include <tabutil.h>
 | 
				
			||||||
#include <text.h>
 | 
					#include <text.h>
 | 
				
			||||||
 | 
				
			|||||||
@ -1,4 +1,5 @@
 | 
				
			|||||||
#include <odbcrset.h>
 | 
					#include <odbcrset.h>
 | 
				
			||||||
 | 
					#include <sqlset.h>
 | 
				
			||||||
#include <utility.h>
 | 
					#include <utility.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
///////////////////////////////////////////////////////////
 | 
					///////////////////////////////////////////////////////////
 | 
				
			||||||
 | 
				
			|||||||
@ -1342,8 +1342,8 @@ void TPrinter::read_configuration(
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
		else
 | 
							else
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
      error_box("Attenzione: la stampante corrente non e' valida.\n"
 | 
					      error_box(TR("Attenzione: la stampante corrente non e' valida. "
 | 
				
			||||||
                "Si prega di selezionare e registrare una nuova stampante.");
 | 
					                   "Si prega di selezionare e registrare una nuova stampante."));
 | 
				
			||||||
      set_printrcd();
 | 
					      set_printrcd();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }           
 | 
					  }           
 | 
				
			||||||
 | 
				
			|||||||
@ -270,6 +270,106 @@ bool TRecordset::save_as_campo(const char* path)
 | 
				
			|||||||
  return !pi.iscancelled();
 | 
					  return !pi.iscancelled();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bool TRecordset::save_as(const char* path, TRecordsetExportFormat fmt, int mode)
 | 
					bool TRecordset::save_as(const char* path, TRecordsetExportFormat fmt, int mode)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
  if (fmt == fmt_unknown)
 | 
					  if (fmt == fmt_unknown)
 | 
				
			||||||
@ -434,7 +534,7 @@ void TRecordset::parsed_text(TString& sql) const
 | 
				
			|||||||
  const bool is_sql = !is_isam;
 | 
					  const bool is_sql = !is_isam;
 | 
				
			||||||
  const char* apici = is_isam ? "\"" : "'";
 | 
					  const char* apici = is_isam ? "\"" : "'";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const bool vars = ((TSQL_recordset*)this)->ask_variables(false);
 | 
					  const bool vars = ((TRecordset*)this)->ask_variables(false);
 | 
				
			||||||
  if (vars) // Se ci sono variabili faccio le sostituzioni
 | 
					  if (vars) // Se ci sono variabili faccio le sostituzioni
 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
    const TString_array& names = variables();
 | 
					    const TString_array& names = variables();
 | 
				
			||||||
@ -672,676 +772,6 @@ bool select_custom_file(TFilename& path, const char* ext, const char* library)
 | 
				
			|||||||
  return ok;
 | 
					  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
 | 
					// TCursor_parser
 | 
				
			||||||
///////////////////////////////////////////////////////////
 | 
					///////////////////////////////////////////////////////////
 | 
				
			||||||
 | 
				
			|||||||
@ -82,44 +82,6 @@ public: // Absolutely needed methods
 | 
				
			|||||||
  virtual ~TRecordset() { }
 | 
					  virtual ~TRecordset() { }
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
///////////////////////////////////////////////////////////
 | 
					 | 
				
			||||||
// TSQL_recordset
 | 
					 | 
				
			||||||
///////////////////////////////////////////////////////////
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class TSQL_recordset : public TRecordset
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
  TString _sql;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  TRecnotype _first_row, _pagesize, _items, _current_row;
 | 
					 | 
				
			||||||
  TArray _column;
 | 
					 | 
				
			||||||
  TArray _page;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
protected:
 | 
					 | 
				
			||||||
  virtual void reset();
 | 
					 | 
				
			||||||
  void parsed_sql_text(TString& sql) const;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
public: // TRecordset
 | 
					 | 
				
			||||||
  virtual void requery();
 | 
					 | 
				
			||||||
  virtual TRecnotype items() const;
 | 
					 | 
				
			||||||
  virtual bool move_to(TRecnotype pos);
 | 
					 | 
				
			||||||
  virtual TRecnotype current_row() const { return _current_row; }
 | 
					 | 
				
			||||||
  virtual unsigned int columns() const;
 | 
					 | 
				
			||||||
  virtual const TRecordset_column_info& column_info(unsigned int c) const;
 | 
					 | 
				
			||||||
  virtual const TVariant& get(unsigned int column) const;
 | 
					 | 
				
			||||||
  const TString& query_text() const { return _sql; }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
public:
 | 
					 | 
				
			||||||
  void set(const char* sql);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  // Internal use only
 | 
					 | 
				
			||||||
  virtual int on_get_items(int argc, char** values, char** columns);
 | 
					 | 
				
			||||||
  virtual int on_get_rows(int argc, char** values);
 | 
					 | 
				
			||||||
  const TArray* row(TRecnotype n);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  TSQL_recordset(const char* sql);
 | 
					 | 
				
			||||||
  virtual ~TSQL_recordset() { }
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
///////////////////////////////////////////////////////////
 | 
					///////////////////////////////////////////////////////////
 | 
				
			||||||
// TISAM_recordset
 | 
					// TISAM_recordset
 | 
				
			||||||
///////////////////////////////////////////////////////////
 | 
					///////////////////////////////////////////////////////////
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										576
									
								
								include/sqlset.cpp
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										576
									
								
								include/sqlset.cpp
									
									
									
									
									
										Executable file
									
								
							@ -0,0 +1,576 @@
 | 
				
			|||||||
 | 
					#include <sqlset.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <diction.h>
 | 
				
			||||||
 | 
					#include <extcdecl.h>
 | 
				
			||||||
 | 
					#include <relation.h>
 | 
				
			||||||
 | 
					#include <progind.h>
 | 
				
			||||||
 | 
					#include <utility.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					///////////////////////////////////////////////////////////
 | 
				
			||||||
 | 
					// 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 << "\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;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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); 
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							
								
								
									
										46
									
								
								include/sqlset.h
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										46
									
								
								include/sqlset.h
									
									
									
									
									
										Executable file
									
								
							@ -0,0 +1,46 @@
 | 
				
			|||||||
 | 
					#ifndef __SQLSET_H
 | 
				
			||||||
 | 
					#define __SQLSET_H
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifndef __RECSET_H
 | 
				
			||||||
 | 
					#include <recset.h>
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					///////////////////////////////////////////////////////////
 | 
				
			||||||
 | 
					// TSQL_recordset
 | 
				
			||||||
 | 
					///////////////////////////////////////////////////////////
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class TSQL_recordset : public TRecordset
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  TString _sql;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  TRecnotype _first_row, _pagesize, _items, _current_row;
 | 
				
			||||||
 | 
					  TArray _column;
 | 
				
			||||||
 | 
					  TArray _page;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					protected:
 | 
				
			||||||
 | 
					  virtual void reset();
 | 
				
			||||||
 | 
					  void parsed_sql_text(TString& sql) const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public: // TRecordset
 | 
				
			||||||
 | 
					  virtual void requery();
 | 
				
			||||||
 | 
					  virtual TRecnotype items() const;
 | 
				
			||||||
 | 
					  virtual bool move_to(TRecnotype pos);
 | 
				
			||||||
 | 
					  virtual TRecnotype current_row() const { return _current_row; }
 | 
				
			||||||
 | 
					  virtual unsigned int columns() const;
 | 
				
			||||||
 | 
					  virtual const TRecordset_column_info& column_info(unsigned int c) const;
 | 
				
			||||||
 | 
					  virtual const TVariant& get(unsigned int column) const;
 | 
				
			||||||
 | 
					  const TString& query_text() const { return _sql; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
					  void set(const char* sql);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Internal use only
 | 
				
			||||||
 | 
					  virtual int on_get_items(int argc, char** values, char** columns);
 | 
				
			||||||
 | 
					  virtual int on_get_rows(int argc, char** values);
 | 
				
			||||||
 | 
					  const TArray* row(TRecnotype n);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  TSQL_recordset(const char* sql);
 | 
				
			||||||
 | 
					  virtual ~TSQL_recordset() { }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
@ -111,7 +111,6 @@ protected:
 | 
				
			|||||||
  bool set_field(const TAS400_column_info& fi, const TVariant& var);
 | 
					  bool set_field(const TAS400_column_info& fi, const TVariant& var);
 | 
				
			||||||
  const TVariant& get_field(const TAS400_column_info& fi) const;
 | 
					  const TVariant& get_field(const TAS400_column_info& fi) const;
 | 
				
			||||||
  
 | 
					  
 | 
				
			||||||
  
 | 
					 | 
				
			||||||
public:
 | 
					public:
 | 
				
			||||||
  virtual TRecnotype new_rec(const char* buf = NULL);
 | 
					  virtual TRecnotype new_rec(const char* buf = NULL);
 | 
				
			||||||
  virtual bool set(const char* column, const TVariant& var);
 | 
					  virtual bool set(const char* column, const TVariant& var);
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user