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);
|
||||
|
||||
TImage* d = new TImage(*i);
|
||||
d->fade_to_gray(COLOR_GRAY);
|
||||
d->fade_to_gray(true);
|
||||
_disabled.add(d, id);
|
||||
|
||||
return id;
|
||||
@ -139,7 +139,7 @@ bool TPicture_array::add(short id)
|
||||
_enabled.add(i, id);
|
||||
|
||||
TImage* d = new TImage(*i);
|
||||
d->fade_to_gray(COLOR_GRAY);
|
||||
d->fade_to_gray(true);
|
||||
_disabled.add(d, id);
|
||||
}
|
||||
else
|
||||
|
@ -99,7 +99,7 @@ bool TGolem_mask::file_handler(TMask_field& f, KEY k)
|
||||
if (!f.empty())
|
||||
{
|
||||
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
|
||||
xvt_fsys_convert_str_to_dir(".", &fs.dir);
|
||||
@ -1177,7 +1177,7 @@ int TMail_messages::get(const char* senderFilter,
|
||||
const MapiRecipDesc* pSender = pMessage->lpOriginator;
|
||||
sender = pSender->lpszName;
|
||||
if (senderFilter && *senderFilter)
|
||||
should_add = stricmp(senderFilter, sender) == 0;
|
||||
should_add = xvt_str_compare_ignoring_case(senderFilter, sender) == 0;
|
||||
}
|
||||
|
||||
if (should_add)
|
||||
|
@ -243,14 +243,10 @@ void TImage::convert_to_default_colors()
|
||||
const COLOR c = xvt_image_get_clut(_image, index) & 0x00FFFFFF;
|
||||
switch (c)
|
||||
{
|
||||
case COLOR_DKCYAN & 0x00FFFFFF:
|
||||
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_GRAY & 0x00FFFFFF:
|
||||
xvt_image_set_clut(_image, index, MASK_DARK_COLOR); break;
|
||||
default:
|
||||
break;
|
||||
case COLOR_DKCYAN & 0x00FFFFFF: 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_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;
|
||||
switch (c)
|
||||
{
|
||||
case COLOR_DKCYAN & 0x00FFFFFF:
|
||||
set_pixel(x, y, MASK_BACK_COLOR); break;
|
||||
case COLOR_CYAN & 0x00FFFFFF:
|
||||
set_pixel(x, y, MASK_LIGHT_COLOR); break;
|
||||
case COLOR_GRAY & 0x00FFFFFF:
|
||||
set_pixel(x, y, MASK_DARK_COLOR); break;
|
||||
default:
|
||||
break;
|
||||
case COLOR_DKCYAN & 0x00FFFFFF: set_pixel(x, y, MASK_BACK_COLOR); break;
|
||||
case COLOR_CYAN & 0x00FFFFFF : set_pixel(x, y, MASK_LIGHT_COLOR); 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
|
||||
void TImage::convert_transparent_color(COLOR transparent)
|
||||
|
||||
// @comm Legge nell'immagine i pixel uguali a quello in alto a sinistra e li setta
|
||||
// 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
|
||||
// in grigio
|
||||
{
|
||||
@ -318,21 +328,14 @@ void TImage::fade_to_gray(int gray)
|
||||
return; // Null image
|
||||
|
||||
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)
|
||||
{
|
||||
for (int index = xvt_image_get_ncolors(_image)-1; index >=0; index--)
|
||||
{
|
||||
COLOR pixie = xvt_image_get_clut(_image, index) & 0x00FFFFFF;
|
||||
if (pixie != trans)
|
||||
{
|
||||
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);
|
||||
}
|
||||
xvt_image_set_clut(_image, index, btncolor2btngray(pixie, use_btn_colors));
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -342,15 +345,7 @@ void TImage::fade_to_gray(int gray)
|
||||
{
|
||||
COLOR pixie = get_pixel(x, y) & 0x00FFFFFF;
|
||||
if (pixie != trans)
|
||||
{
|
||||
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);
|
||||
}
|
||||
set_pixel(x, y, btncolor2btngray(pixie, use_btn_colors));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -86,7 +86,7 @@ public:
|
||||
|
||||
// Trasforma l'immagine in toni di grigio.
|
||||
// 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>
|
||||
void set_clut(byte n, COLOR c);
|
||||
|
@ -8,11 +8,9 @@
|
||||
#include <execp.h>
|
||||
#include <expr.h>
|
||||
#include <msksheet.h>
|
||||
//#include <prefix.h>
|
||||
#include <recarray.h>
|
||||
#include <recset.h>
|
||||
#include <relapp.h>
|
||||
//#include <relation.h>
|
||||
#include <sheet.h>
|
||||
#include <tabutil.h>
|
||||
#include <text.h>
|
||||
|
@ -1,4 +1,5 @@
|
||||
#include <odbcrset.h>
|
||||
#include <sqlset.h>
|
||||
#include <utility.h>
|
||||
|
||||
///////////////////////////////////////////////////////////
|
||||
|
@ -1342,8 +1342,8 @@ void TPrinter::read_configuration(
|
||||
}
|
||||
else
|
||||
{
|
||||
error_box("Attenzione: la stampante corrente non e' valida.\n"
|
||||
"Si prega di selezionare e registrare una nuova stampante.");
|
||||
error_box(TR("Attenzione: la stampante corrente non e' valida. "
|
||||
"Si prega di selezionare e registrare una nuova stampante."));
|
||||
set_printrcd();
|
||||
}
|
||||
}
|
||||
|
@ -270,6 +270,106 @@ bool TRecordset::save_as_campo(const char* path)
|
||||
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)
|
||||
{
|
||||
if (fmt == fmt_unknown)
|
||||
@ -434,7 +534,7 @@ void TRecordset::parsed_text(TString& sql) const
|
||||
const bool is_sql = !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
|
||||
{
|
||||
const TString_array& names = variables();
|
||||
@ -672,676 +772,6 @@ bool select_custom_file(TFilename& path, const char* ext, const char* library)
|
||||
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
|
||||
///////////////////////////////////////////////////////////
|
||||
|
@ -82,44 +82,6 @@ public: // Absolutely needed methods
|
||||
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
|
||||
///////////////////////////////////////////////////////////
|
||||
|
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
|
@ -110,7 +110,6 @@ protected:
|
||||
TAS400_column_info* parse_field(const char* column, int& c, bool create);
|
||||
bool set_field(const TAS400_column_info& fi, const TVariant& var);
|
||||
const TVariant& get_field(const TAS400_column_info& fi) const;
|
||||
|
||||
|
||||
public:
|
||||
virtual TRecnotype new_rec(const char* buf = NULL);
|
||||
|
Loading…
x
Reference in New Issue
Block a user