Italian community of Lazarus and Free Pascal

Programmazione => Databases => Topic aperto da: petrusic - Aprile 10, 2021, 05:05:41 pm

Titolo: leggere tabelle di DB SQLite3 con Zeos
Inserito da: petrusic - Aprile 10, 2021, 05:05:41 pm
Ho bisogno di risalire all'ultimo numero progressivo di record di diverse tabelle di DB Sqlite3.
Da quando ho cominciato a muovermi in lazarus, affidandomi a Zeoslib, ho difficoltà ad eseguire una open della connessione al DB per eseguire successivamente le Query, Insert, Update, Delete che servono. Infine,  comandare una unica CommitUpdates o CancelUpdates alla fine di ciascun ramo logico di operazioni.
Zeoslib non permette di eseguire la Open della connessione al DB, ma alla singola Query. L'oggetto ZQuery è l'unico agganciato alla procedura Open.
Pertanto il codice che segue funziona solo per la prima tabella. dopo di che, il programma innesca una sorta di ciclo infinito.
Codice: [Seleziona]
 sql:= 'SELECT movimgg.IdMovvgg AS FROM movimgg ORDER BY IdMovvgg DESC LIMIT 1';
  Form1.ZQuery1.SQL.Text:= sql;
  Form1.ZQuery1.Open;
  Form1.ZQuery1.First;
  movimggNumId:= StrToInt(Form1.ZQuery1.FieldByName('IdMovvgg').AsString);

  sql:= 'SELECT IdPartGlob FROM partmovv ORDER BY IdPartGlob DESC LIMIT 1';
  Form1.ZQuery1.SQL.Text:= sql;
  Form1.ZQuery1.ExecSQL;
  Form1.ZQuery1.First;
  partmovNumId:= StrToInt(Form1.ZQuery1.FieldByName('IdPartGlob').AsString);

  sql := 'SELECT IdPrestMom FROM prestmom ORDER BY IdPrestMom DESC LIMIT 1';
  Form1.ZQuery1.SQL.Text := sql;
  Form1.ZQuery1.ExecSQL;
  Form1.ZQuery1.First;
  prestmomNumId:= StrToInt(Form1.ZQuery1.FieldByName('IdPrestMom').AsString);

  sql := 'SELECT IdResCasgg FROM restacassagg ORDER BY IdResCasgg DESC LIMIT 1';
  Form1.ZQuery1.SQL.Text := sql;
  Form1.ZQuery1.ExecSQL;
  Form1.ZQuery1.First;
  restaCassaNumId:= StrToInt(Form1.ZQuery1.FieldByName('IdResCasgg').AsString);     

Posso risolvere in qualche modo o devo creare tante query quanti sono gli accessi caldi al DB, prima di andare avanti

Penso infatti a come dovrei procedere quando dovrò inserire righe in tabelle diverse, per confermarle o annullarle alla fine di tutti gli inserimenti, pertinenti allo stesso insieme di dati.
Titolo: Re:leggere tabelle di DB SQLite3 con Zeos
Inserito da: xinyiman - Aprile 10, 2021, 06:45:35 pm
Prova così

Codice: [Seleziona]
 sql:= 'SELECT movimgg.IdMovvgg AS FROM movimgg ORDER BY IdMovvgg DESC LIMIT 1';
  Form1.ZQuery1.SQL.Text:= sql;
  Form1.ZQuery1.Open;
  Form1.ZQuery1.First;
  movimggNumId:= StrToInt(Form1.ZQuery1.FieldByName('IdMovvgg').AsString);
  Form1.ZQuery1.Close;

  sql:= 'SELECT IdPartGlob FROM partmovv ORDER BY IdPartGlob DESC LIMIT 1';
  Form1.ZQuery1.SQL.Text:= sql;
  Form1.ZQuery1.Open;
  Form1.ZQuery1.First;
  partmovNumId:= StrToInt(Form1.ZQuery1.FieldByName('IdPartGlob').AsString);
  Form1.ZQuery1.Close;

  sql := 'SELECT IdPrestMom FROM prestmom ORDER BY IdPrestMom DESC LIMIT 1';
  Form1.ZQuery1.SQL.Text := sql;
  Form1.ZQuery1.Open;
  Form1.ZQuery1.First;
  prestmomNumId:= StrToInt(Form1.ZQuery1.FieldByName('IdPrestMom').AsString);
  Form1.ZQuery1.Close;

  sql := 'SELECT IdResCasgg FROM restacassagg ORDER BY IdResCasgg DESC LIMIT 1';
  Form1.ZQuery1.SQL.Text := sql;
  Form1.ZQuery1.Open;
  Form1.ZQuery1.First;
  restaCassaNumId:= StrToInt(Form1.ZQuery1.FieldByName('IdResCasgg').AsString);     
  Form1.ZQuery1.Close;

Quando apri un recordset, per poterlo riutilizzare devi chiudere quello precedente.
Poi EXECSQL si usa per eseguire una sql che non ritorna valori, se devi recuperare valori devi usare la open (e quindi la close di conseguenza).
Titolo: Re:leggere tabelle di DB SQLite3 con Zeos
Inserito da: DragoRosso - Aprile 10, 2021, 10:05:11 pm
In generale è buona cosa come ha detto @xinyiman:

1) Chiudere la query;
2) Impostare la sentenza SQL;
3) Aprire la query;

Per Aprire e Chiudere, oltre a Open e Close, si può usare la proprietà Active (True e False).
Giusto per la cronaca, ma sconsigliato, si può anche solo impostare la proprietà SQL e poi settare la proprità Active a True senza fare il Close o settare Active a False: ZEOS "chiude" la query automaticamente se si setta la proprietà SQL.

Invece è interessante la necessità riguardante la validazione di una serie di "commit" di un insieme di transazioni singole.
Varie scritture su varie tabelle sono molte volte necessarie per completare un sessione di aggiornamento.
Ma effettivamente c'è uno "strumento" che consente di effettuare un rollback di tutte le transazioni interessate nel caso qualcosa non vada a buon fine?

In tutti questi anni a me non è mai servito, o meglio non è mai successo alcunchè per cui fosse necessario effettuare un rollback, ma effettivamente se ci fosse lo implementerei sicuramente.

Titolo: Re:leggere tabelle di DB SQLite3 con Zeos
Inserito da: petrusic - Aprile 10, 2021, 10:08:54 pm
Quando apri un recordset, per poterlo riutilizzare devi chiudere quello precedente.
Non lo sapevo di certo, ma l'avevo immaginato. Secondo me, Gambas lavora meglio coi DB.

Poi EXECSQL si usa per eseguire una sql che non ritorna valori, se devi recuperare valori devi usare la open (e quindi la close di conseguenza).
Questo non lo sapevo proprio. Grazie.

Tornando al mio problema, Si, come mi hai suggerito, funziona. l'ho già provato nel pomeriggio, prima che arrivasse la tua risposta. Perciò questa parte di programma posso anche lasciarla come l'ho già modificata, conformemente al tuo suggerimento.

Tuttavia, non risolverei del tutto. Infatti, mi trovo di fronte alla seguente necessità:
1) Devo Inserire righe nuove in una 1.a  tabella (movimgg)
2) Devo Inserire righe nuove in una 2.a  tabella (prestmom)
3) Devo Inserire righe nuove in una 3.a  tabella (restacassagg)
4) Devo Inserire righe nuove in una 4.a  tabella (riepmovg)
5) Devo anche aggiornare 2 campi di un'ulteriore 5.a  tabella (piancont)

Ma, poichè tutti i dati da inserire hanno la stessa data di calendario, non posso eseguire tante Open  con altrettante Close distinte, perchè con la Close, da quanto ho capito, si scatena un Commit automatico. E se mi trovassi, per la manifestazione di un casuale errore, nella necessità di operare un rollback, non sarei più in grado di farlo.

Una realtà del genere significherebbe, per me, dover rinunziare a Zeoslib ed orientarmi su qualcos'altro. Al punto in cui sono arrivato, sarebbe un vero disastro in termini di tempo ed impegno.
Spero tanto che ci sia una via d'uscita pratica ed efficace.

Titolo: Re:leggere tabelle di DB SQLite3 con Zeos
Inserito da: petrusic - Aprile 10, 2021, 10:19:54 pm
è interessante la necessità riguardante la validazione di una serie di "commit" di un insieme di transazioni singole.
Varie scritture su varie tabelle sono molte volte necessarie per completare un sessione di aggiornamento.
Ma effettivamente c'è uno "strumento" che consente di effettuare un rollback di tutte le transazioni interessate nel caso qualcosa non vada a buon fine?

Molto appetibile questa tua indicazione, ma dove potrei trovare la spiegazione sull'argomento?

In tutti questi anni a me non è mai servito, o meglio non è mai successo alcunchè per cui fosse necessario effettuare un rollback, ma effettivamente se ci fosse lo implementerei sicuramente.
Personalmente, non mi trovi d'accordo sull'aspettare che l'evento negativo si manifesti per gestirlo. Occorre, secondo me, prevederlo sin dalla progettazione ed inserirne il relativo codice, alfine di non doverci tornare sopra a distanza di tempo, dopo avere, magari, dimenticato la logica dei dettagli del codice scritto.
Titolo: Re:leggere tabelle di DB SQLite3 con Zeos
Inserito da: DragoRosso - Aprile 10, 2021, 10:46:00 pm
Molto appetibile questa tua indicazione, ma dove potrei trovare la spiegazione sull'argomento?
Non lo sò, era la mia appunto una domanda rivolta alla community.

Personalmente, non mi trovi d'accordo sull'aspettare che l'evento negativo si manifesti per gestirlo. Occorre, secondo me, prevederlo sin dalla progettazione ed inserirne il relativo codice, alfine di non doverci tornare sopra a distanza di tempo, dopo avere, magari, dimenticato la logica dei dettagli del codice scritto.
Assolutamente d'accordo con te. Per ovviare a ciò, in tutte gli eventi di scrittura (tipo beforepost), ho sempre "super controllato" tutti i dati. E in tutti questi anni non è mai successo niente sui database dei miei clienti. Certo l'interruzione di corrente è sempre in agguato, lo spegnimento inopportuno del PC (foss'anche un server) anche, come pure un blocco anomalo.

Sino ad ora non avevo trovato nessuna funzione che mi consentisse di gestire queste eventuali situazioni (e non penso ce ne siano a meno di andare con sistemi a pagamento professionali). Però almeno se ci fosse qualche funzionalità che protegga in maniera normale, come una "sessione" ad esempio, la userei.

Non sono un superesperto di database, ho usato ISAM (anni 80), poi DBASE, Paradox, MSDE, MS SQL Express ed infine SQLite. Li uso per scopi industriali (compreso qualche anagrafica), ma non certo a livello gestionale come in un sistema contabile.

Vediamo se qualcuno con più esperienza sui database ci può illuminare.

Saluti
Titolo: Re:leggere tabelle di DB SQLite3 con Zeos
Inserito da: Stilgar - Aprile 10, 2021, 11:22:46 pm
Ciao.
Vedendo i frammenti di codice, mi verrebbe da suggerirti http://www.lazaruspascal.it/index.php?page=111 (http://www.lazaruspascal.it/index.php?page=111).
Se utilizzi il codice per manipolare il codice e non ti piace lavorare con l'open/close e altre cose ereditate da Delphi, utilizzando le interfacce puoi addirittura non distruggere le istanze degli oggetti (uscendo dallo scope di esecuzione, se possibile, vengono distrutti in automatico).
Tramite le interfacce puoi eseguire le begin transaction e commit/rollback a seconda delle esigenze.
Codice: [Seleziona]
     
Type
IZConnection = interface  [....]  procedure Commit;      procedure Rollback;    procedure PrepareTransaction(const transactionid: string);       
  procedure CommitPrepared(const transactionid: string);       
  procedure RollbackPrepared(const transactionid: string); procedure Commit; 
  [....]
Caso tipico, inizi la transazione, tra il try ed exception metti le tue operazioni a db, poi se tutto va bene chiami la commit o se ci sono eccezioni la rollback.
Vedendo le istruzioni di sqlite, potresti fare delle chimate dirette al motore con
Codice: [Seleziona]
 
function Execute(const SQL: ZAnsiString): Boolean; overload;
per gestire a tuo piacimento le transazioni.
https://sqlite.org/lang_transaction.html (https://sqlite.org/lang_transaction.html)

Dal componente della connessione visuale puoi ottenere l'interfaccia alla connessione.
Poi puoi fare quel che vuoi.
https://github.com/delphipascal/zeoslib/blob/master/zeos-7.0.4/src/dbc/ZDbcIntfs.pas (https://github.com/delphipascal/zeoslib/blob/master/zeos-7.0.4/src/dbc/ZDbcIntfs.pas)

Se vuoi utilizzare solo componenti visuali, dovresti (stando alla filosofia dei componenti) definirti un'oggetto query per ogni query che pensi dover lanciare.
Personalmente, visto che scrivi le query nel codice, ti suggerisco la strada a "basso" livello con Zeos.


Titolo: Re:leggere tabelle di DB SQLite3 con Zeos
Inserito da: DragoRosso - Aprile 11, 2021, 12:20:07 am
@Stilgar

Quindi, per farla semplice in modo quasi universale, uno potrebbe fare prima della "prima" scrittura un SAVEPOINT nominato, poi fare le scritture successive e se qualcosa và male fa un ROLLBACK TO ... e non prosegue, se invece alla fine è andato tutto OK fà un RELEASE.

Interessante, non avevo mai approfondito l'argomento.

Provo subito con alcuni progetti.

Grazie mille


Titolo: Re:leggere tabelle di DB SQLite3 con Zeos
Inserito da: petrusic - Aprile 11, 2021, 05:41:33 pm
@ Stilgar
Si, hai capito perfettamente. Non sono abituato a lavorare sui DB tramite open/close. Mi piace prepararmi la mia brava riga col comando SQLite e mandarla in esecuzione, praticamente, in via transazionale.
Ho riletto l'argomento che mi hai suggerito:
Vedendo i frammenti di codice, mi verrebbe da suggerirti http://www.lazaruspascal.it/index.php?page=111 (http://www.lazaruspascal.it/index.php?page=111).
Devo dire che non mi trovo con la terminologia ivi contenuta, perchè non vi sono abituato, Non capisco infatti quando dici:
Citazione
Per "accendere" il motore di ZEOS Low Level, basta una semplice chiamata:
var Connection : IZConnection;
..
  Connection := DriverManager.GetConnection(connectionURL);
La riga "var Connection:  IZConnection;" dovrebbe corrispondere alla " ZConnection1: TZConnection;" contenuta  nella sezione Interface del mio programma:
Codice: [Seleziona]
interface

uses
  Classes, SysUtils, DB, Forms, Controls, Graphics, Dialogs, StdCtrls, ComCtrls,
  ExtCtrls, fpspreadsheet, laz_fpspreadsheet, Process, ZConnection, ZDataset, utilmie,
  fpstypes; // , flcDynArrays; dovrebbe permettere di riordinare la seuenza degli elementi di un vettore

type

  { TForm1 }
  TForm1 = class(TForm)

   DataSource1: TDataSource;
    . . .
    ZConnection1: TZConnection;
    ZQuery1: TZQuery;
    . . .
La riga " Connection := DriverManager.GetConnection(connectionURL);" non la capisco, perchè non capisco Cosa chiami con "DriverManager".
 "DriverManager"  è forse il "DataSource1" che ho dichiarato sempre nella sezione Interface (cioè, uno degli oggetti di Zeos?)?
Prima di proseguire, ho bisogno di capire bene i riferimenti principali che permettono al programma utente di agganciarsi alle funzioni ZEOS.
Titolo: Re:leggere tabelle di DB SQLite3 con Zeos
Inserito da: Stilgar - Aprile 11, 2021, 05:57:03 pm
Ciao.

Zeos è costruito a strati.

Quello che hai visto fino ad adesso è quello più esterno.

Il DriverManager è proprio una classe per i fatti suoi.

Nel tuo codice devi "adattare" il codice utilizzando:
Codice: [Seleziona]
var Connection:  IZConnection;
....
  Connection :=  ZConnection1.DbcConnection;
...



Quando indico "accendere" il motore, intendo dire attivare la connessione al database con tutto quello che ne consegue. (Caricamento della dll del driver vendor per l'accesso e altre cosine che fa internamente per garantirti l'accesso al database)

Ho cercato di non essere pesante ed entrare troppo nei dettagli ma dare un taglio più informare giusto per arrivare a come utilizzare le cose. Gli sviluppatori Java non hanno problemi di sorta con quelle interfacce, essendo simili a quelle di JDBC.

Stilgar
Titolo: Re:leggere tabelle di DB SQLite3 con Zeos
Inserito da: petrusic - Aprile 12, 2021, 12:55:47 pm
Quando indico "accendere" il motore, intendo dire attivare la connessione al database con tutto quello che ne consegue. (Caricamento della dll del driver vendor per l'accesso e altre cosine che fa internamente per garantirti l'accesso al database)

Ho cercato di non essere pesante ed entrare troppo nei dettagli ma dare un taglio più informare giusto per arrivare a come utilizzare le cose. Gli sviluppatori Java non hanno problemi di sorta con quelle interfacce, essendo simili a quelle di JDBC.
Mi dispiace deluderti, ma non ho la preparazione che forse immagini.
Purtroppo, per capire, a causa della scarnissima documentazione trovata, navigando, posso solo  chiedere, e poi chiedere, e poi  . . .  ancora chiedere

Zeos è costruito a strati.
Quello che hai visto fino ad adesso è quello più esterno.

Il DriverManager è proprio una classe per i fatti suoi.
Nel tuo codice devi "adattare" il codice utilizzando:
[/size]
Codice: [Seleziona]
var Connection:  IZConnection;
....
  Connection :=  ZConnection1.DbcConnection;
...

A me non funziona:
Ho modificato la parte implementation del programma inserendovi la var Connection:
Codice: [Seleziona]
unit frmmain;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, DB, Forms, Controls, Graphics, Dialogs, StdCtrls, ComCtrls,
  ExtCtrls, fpspreadsheet, laz_fpspreadsheet, Process, ZConnection, ZDataset, utilmie,
  fpstypes; // , flcDynArrays; dovrebbe permettere di riordinare la seuenza degli elementi di un vettore

type

  { TForm1 }
{
--- bla --- bla ---
  TForm1 = class(TForm)

    DataSource1: TDataSource;
    leInfoMov: TLabeledEdit;
    PulsFine: TButton;
    PulsAvvia: TButton;
    Label1: TLabel;
    lsDir: TListBox;
    ZConnection1: TZConnection;
    ZQuery1: TZQuery;
    procedure PulsFineClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure PulsAvviaClick(Sender: TObject);
  end;

var
  Form1: TForm1;
implementation

{$R *.lfm}
uses
  frm2;

type
   TsSpreadsheetFormat = (sfExcel2, sfExcel5, sfExcel8, sfExcelXML, sfOOXML,
    sfOpenDocument, sfCSV, sfHTML, sfWikiTable_Pipes, sfWikiTable_WikiMedia, sfUser);   // sfOpenDocument è il tipo di formato valido per OpenOffice/LibreOffice.ods

const
    dirFogli: string = '/media/dirdati/dativari/ContDom/';

var

  Connection: IZConnection;
{ TForm1 }                               
--- bla --- bla ---
Il compilatore accende il seguente messaggio d'Errore:
Citazione
frmmain.pas(75,15) Error: Identifier not found "IZConnection"
frmmain.pas(75,27) Error: Error in type definition
Titolo: Re:leggere tabelle di DB SQLite3 con Zeos
Inserito da: DragoRosso - Aprile 12, 2021, 02:11:26 pm
La "IZConnection" è definita nell'unità "ZDbcIntfs" che devi inserire nelle Uses nella sezione Interface.
Titolo: Re:leggere tabelle di DB SQLite3 con Zeos
Inserito da: DragoRosso - Aprile 12, 2021, 02:39:50 pm
@petrusic
Il discorso fatto @Stilgar, è per facilitare il controllo delle funzionalità per la gestione del database.
Usando le "interfacce" dirette, puoi accedere alle proprietà mascherate ed effettuare tutta una serie di operazioni che altrimenti sarebbe un pò più laboraioso fare.

Che differenza c'è tra l'uso di "componenti" e "interfacce"? Andando al sodo terra terra, diciamo che i componenti sono degli oggetti con funzionalità base (o meglio con le funzionalità che chi li ha creati pensa siano sufficienti) per svolgere nella maniera più semplice il proprio compito.

In genere i componenti hanno una controparte di "design" che consente di essere configurati a designtime ed evitando tutte quelle operazioni che si dovrebbero invece effettuare a codice. I componenti possono anche essere dichiarati e costruiti a codice (a runtime quindi), normalmente bisogna però porre attenzione a settare le proprietà nella giusta sequenza.
I componenti hanno dei limiti, che sono essenzialmente quelli legati alla facilità d'uso. E alcune volte anche dei limiti nell'uso in applicazioni multithread.

Usando invece le interfacce, si ha accesso quasi completo alla funzionalità, migliorando l'efficienza. Come controaltare ho che le "interfacce" possono essere usate solo a codice (quindi "costruite" a runtime). Inoltre è necessario settare ogni singola proprietà conformemente all'uso, là dove il componente invece già svolgeva tale compito in modo mascherato.

Per chi non ha molta esperienza, e per chi non ha la possibilità di approfondire l'argomento, è consigliato l'uso dei componenti, magari piano piano costruiti a runtime invece che a designtime.

Saluti
Titolo: Re:leggere tabelle di DB SQLite3 con Zeos
Inserito da: petrusic - Aprile 12, 2021, 03:26:50 pm
La "IZConnection" è definita nell'unità "ZDbcIntfs" che devi inserire nelle Uses nella sezione Interface.
Hai ragione, era scritto nella guida che mi ha suggerito di leggere Stilgar. Ora il compilatore l'ha riconosciuto.

In genere i componenti hanno una controparte di "design" che consente di essere configurati a designtime ed evitando tutte quelle operazioni che si dovrebbero invece effettuare a codice.
. . .

Ti ringrazio per la tua riflessione. In realtà non ho esperienza di interfacce grafiche per la gestione di DB e l'unico tipo di DB che conosco è SQLite, anche se professionalmente, tanti anni fa, ormai, ho conosciuto il CICS IBM ed ho imparato a costruire Database relazionali.
Con Gambas ho sempre gestito gli accessi a DB tramite codice, mai con strumenti da interfacciamento grafico.

Tornando alla tua riflessione devo dire che mi aspettavo di leggere che, utilizzando i componenti, non fosse necessario ricorrere al loro design.
 :D
Invece, per quanto capisco, non posso fare a meno di mettere in campo quel minimo sindacale, costituito da  TDataSource, TZquery e TZConnection.
Titolo: Re:leggere tabelle di DB SQLite3 con Zeos
Inserito da: DragoRosso - Aprile 12, 2021, 06:45:27 pm
@petrusic
Mio consiglio personale è di usare i componenti per quanto possibile, a meno che non ci siano necessità particolari, come prestazioni, multithreading spinto, limite all'uso delle risorse, etc ...

Principalmente sono due i motivi:

1) Compatibilità con il passato, presente e futuro: i componenti fatti bene funzioneranno ieri come domani, perchè chi li programma ha normalmente in mente il target a cui il componente deve arrivare. Inoltre innovare un componente mantenendolo perfettamente compatibile con il passato è relativamente semplice.

2) In genere i componenti sono pensati per evitare tutte le problematiche che potrebbero accadere: esempio è il settaggio di una proprietà che và a modificarne altre due.

Non è detto che le interfacce non abbiano lo stesso tali caratteristiche, ma esponendo il cuore del funzionamento è più probabile che siano meno retro comaptibili.

Io uso ormai da diversi anni SQLite, sicuramente non al 100% delle sue possibilità (vedi post arretrati). E quando connetto un database con un componente come TZConnection, poi uso tante query, table, dataset ....

In genere uno per ogni esigenza. Se ho quattro tabelle in uso, io creo 4 componenti query che operano in maniera autonoma (ma comunque sincronizzata in scrittura, perchè bisogna ricordarsi che SQLite è "monoscrittura").

Perchè devo aprire e chiudere continuamente le query se opero con diverse tabelle ? Genero una query e quella rimane attiva finchè lo decido io, perche su un'altra tabella userò un altro componente query.

Non c'è però da preoccuparsi, la tecnica arriva con l'esperienza e i progetti (almeno, questa è la mia storia) vivono di vita propria, evolvono con l'esperienza di chi li ha creati e li mantiene.

Saluti
Titolo: Re:leggere tabelle di DB SQLite3 con Zeos
Inserito da: petrusic - Aprile 12, 2021, 10:02:49 pm
Bene.
Per ora preferisco muovermi come posso farlo, con le mie forze e con le conoscenze che via sto acquisendo qui, grazie ai vostri preziosi suggerimenti.

Ho continuato a seguire l'argomento "Zeos ... dietro le quinte" e ho portato le modifiche necessarie al mio programma, ma, ahimè, mi sono imbattuto in un muovo stop del compilatore:
Codice: [Seleziona]
procedure TForm1.FormCreate(Sender: TObject);
const
  dbCopia: String = '/media/dirdati/dativari/contabfam/ContabFamdb_prove';
  dbOrig: String = '/media/dirdati/dativari/contabfam/ContabFamdb';

var
  dbConn: IZConnection;
  stmt: IZStatement;
  rs: IZResultSet;
  swAster: Boolean = False;

  lun, totRecQry: Integer;

  annata, dtIni, dtFin, sql, striMia: String;
  dbDati: String = 'zdbc:sqlite-3:';
[...]
begin 
[...]
  dbDati:= dbDati + dbCopia;
  dbConn := DriverManager.GetConnection(dbDati);           
[...]
   stmt:= FConnection.CreateStatement;
   sql:= 'SELECT DtCoMovg,StaDtMovg FROM riepmovg WHERE DtCoMovg >= ' + dtIni + ' AND DtCoMovg <= ' + dtFin;
   rs:= stmt.ExecuteQuery(sql);
   totRecQry:= rs.GetRow;
[...]                                                       
Non capisco perchè il compilatore NON riconosce FConnestion:
Citazione
frmmain.pas(155,17) Error: Identifier not found "FConnection"
Ho guardato e riguardato la guida di Stilgar tante volte, ma non ho trovato niente che mi illuminasse.
Che cosa manca ora?
Titolo: Re:leggere tabelle di DB SQLite3 con Zeos
Inserito da: Stilgar - Aprile 12, 2021, 11:45:41 pm
:)
Attento ai nomi delle variabili.


Il compilatore ti dice cosa non gradisce.


Stai dichiarando dbConn: IZConnection; e usi [/size]stmt:= FConnection.CreateStatement;.


"F" prima delle variabili, se applichiamo le convenzioni, indica che è una variabile d'istanza.


dbConn va benissimo come nome locale.


:)




Stilgar
Titolo: Re:leggere tabelle di DB SQLite3 con Zeos
Inserito da: petrusic - Aprile 13, 2021, 04:39:17 pm
:)
Stai dichiarando dbConn: IZConnection; e usi [/size]stmt:= FConnection.CreateStatement;.
"F" prima delle variabili, se applichiamo le convenzioni, indica che è una variabile d'istanza.
dbConn va benissimo come nome locale.

Non ti nascondo che ho qualche difficoltà a capire, per i mie limiti, il tuo suggerimento. Io non ho fatto altro che copiare i nomi di alcune variabili dalla tua miniguida.
Comunque, ho modificato le righe  che hanno determinato l'errore segnalato dal compilatore:
Codice: [Seleziona]
procedure TForm1.FormCreate(Sender: TObject);
const
  dbCopia: String = '/media/dirdati/dativari/contabfam/ContabFamdb_prove';
  dbOrig: String = '/media/dirdati/dativari/contabfam/ContabFamdb';

var
  dbConn: IZConnection;
  stmt: IZStatement;
  rs: IZResultSet;

  lun, totRecQry: Integer;

  annata, dtIni, dtFin, sql, striMia: String;
  dbDati: String = 'zdbc:sqlite-3:';

begin                                 
[...]
 dbDati:= dbDati + dbCopia;
 dbConn:= DriverManager.GetConnection(dbDati);                                 
[...]
 stmt:= dbConn.CreateStatement;
 sql:= 'SELECT DtCoMovg,StaDtMovg FROM riepmovg WHERE DtCoMovg >= ' + dtIni + ' AND DtCoMovg <= ' + dtFin;
 rs:= stmt.ExecuteQuery(sql);
 totRecQry:= rs.GetRow;           
[...]
Ebbene, il compilatore non ha trovato errori formali, ma l'esecuzione si è fermata alla riga:
"stmt:= dbConn.CreateStatement;", segnalando l'Errore: "SQL Error: unable to open database file".
 :o ?  :o
Titolo: Re:leggere tabelle di DB SQLite3 con Zeos
Inserito da: DragoRosso - Aprile 13, 2021, 05:34:38 pm

const
  dbCopia: String = '/media/dirdati/dativari/contabfam/ContabFamdb_prove';


Manca l'estensione del file ?

Magari ho fatto una osservazione sciocca, perdonatemi.

Saluti
Titolo: Re:leggere tabelle di DB SQLite3 con Zeos
Inserito da: nomorelogic - Aprile 13, 2021, 06:02:46 pm
hai controllato i permessi di tutto il percorso?

il tuo utente deve avere i permessi di lettura/scrittura sulle varie cartelle
Titolo: Re:leggere tabelle di DB SQLite3 con Zeos
Inserito da: Stilgar - Aprile 13, 2021, 06:32:51 pm
Drago.
A Sqlite non interessa l'estensione.
Effettivamente potrebbe essere che manchi qualche cartella o non sia raggiungibile causa permessi.




Stilgar

Titolo: Re:leggere tabelle di DB SQLite3 con Zeos
Inserito da: Stilgar - Aprile 13, 2021, 06:44:57 pm
Ho verificato (giusto perchè mi è venuto il dubbio).
Sotto windows creo file con o senza estensione.




Stilgar
Titolo: Re:leggere tabelle di DB SQLite3 con Zeos
Inserito da: DragoRosso - Aprile 13, 2021, 06:53:49 pm
Si, lo sò. Intendevo che la path includeva il file, ma il file reale aveva anche una estensione tipo .s3db ......
Titolo: Re:leggere tabelle di DB SQLite3 con Zeos
Inserito da: petrusic - Aprile 14, 2021, 09:49:22 am
Dalle osservazioni lette capisco che le istruzioni che ho allegato in visione
Codice: [Seleziona]
stmt:= dbConn.CreateStatement;
 sql:= 'SELECT DtCoMovg,StaDtMovg FROM riepmovg WHERE DtCoMovg >= ' + dtIni + ' AND DtCoMovg <= ' + dtFin;
 rs:= stmt.ExecuteQuery(sql);
 totRecQry:= rs.GetRow;     
sono formalmente corrette.

hai controllato i permessi di tutto il percorso?
il tuo utente deve avere i permessi di lettura/scrittura sulle varie cartelle
No, l'unico utente sono io. Comunque, per prova ho attivato i permessi a tutti e l'errore è sempre lo stesso
Citazione
SQL Error: unable to open database file".
al momento del "dbConn.CreateStatement".
Titolo: Re:leggere tabelle di DB SQLite3 con Zeos
Inserito da: nomorelogic - Aprile 14, 2021, 12:50:08 pm
puoi mettere quì l'output del seguente comando?

Codice: [Seleziona]
namei -m /media/dirdati/dativari/contabfam/ContabFamdb_prove
Titolo: Re:leggere tabelle di DB SQLite3 con Zeos
Inserito da: petrusic - Aprile 14, 2021, 03:47:48 pm
Codice: [Seleziona]
[petrus@localhost ~]$ namei -m /media/dirdati/dativari/contabfam/ContabFamdb_prove
f: /media/dirdati/dativari/contabfam/ContabFamdb_prove
 dr-xr-xr-x /
 drwxr-xr-x media
 drwxr-xr-x dirdati
 drwxrwxrwx dativari
 drwxrwxrwx contabfam
 -rwxr-xr-x ContabFamdb_prove
[petrus@localhost ~]$

Non so se possa servire, ma il programma su cui stiamo ragionando, prima di tutto, esegue una copia del db originale in quello di prova:
Codice: [Seleziona]
 dbCopia: String = '/media/dirdati/dativari/contabfam/ContabFamdb_prove';
 dbOrig: String = '/media/dirdati/dativari/contabfam/ContabFamdb';
var
  AProcess: TProcess;
  Esito: TSearchRec;

  dbConn: IZConnection;
  stmt: IZStatement;
  rs: IZResultSet;

  lun, totRecQry: Integer;

  annata, dtIni, dtFin, sql, striMia: String;
  dbDati: String = 'zdbc:sqlite-3:';

begin
 AProcess := TProcess.Create(nil);      //  creo l'oggetto TProcess e lo assegno alla variabile AProcess.
  striMia:='rm -f ' + dbCopia;       // *** NON necessasria ***
  AProcess.CommandLine:= striMia;   // rimuovo, se esiste, la copia vecchia del DB
  AProcess.Options := AProcess.Options + [poWaitOnExit];    //  Questa opzione permette il complitamento del comando affidato ad AProcess prima di
//                                                            restituire il controllo al programma corrente
  AProcess.Execute;   //  esegue la linea di comando col comando creata prima
  striMia:= 'cp ' + dbOrig + ' ' + dbCopia;
  AProcess.CommandLine:= striMia;
  AProcess.Options := AProcess.Options + [poWaitOnExit];
  AProcess.Execute;
  AProcess.Free;
  ShowMessage('"ContabFamdb" copiato in "ContabFamdb_prove"');     
e funziona benissimo
Titolo: Re:leggere tabelle di DB SQLite3 con Zeos
Inserito da: nomorelogic - Aprile 14, 2021, 03:58:35 pm

non sapevo della copia che funzionava
cmq "unable to open database file" mi sembra proprio un errore di nome di file o di permessi
le cartelle media e dirdati, immagino siano del tuo utente e non di root giusto?


un piccolo suggerimento
non hai bisogno di usare TProcess che chiama 'rm' per eliminare un file, c'è DeleteFile
https://www.freepascal.org/docs-html/rtl/sysutils/deletefile.html

per copiare c'è CopyFile che se non ricordo male è in FileUtils
Titolo: Re:leggere tabelle di DB SQLite3 con Zeos
Inserito da: petrusic - Aprile 14, 2021, 05:33:43 pm
Ho aggiunto il gruppo di istruzioni try nel CreateStatement, allo scopo di verificare il contenuto del parametro dbDati al momento dell'esecuzione:
Codice: [Seleziona]
try
          stmt:= dbConn.CreateStatement;
        except
          on E: Exception do
          WriteLn('ERRORE nel "dbConn.CreateStatement", dove dbConn contiene "DriverManager.GetConnection(' + dbDati + ')"' + IntToStr(E.HelpContext) + ': ' + E.Message);
        end;
Naturalmente non è cambiato niente.
Citazione

ERRORE nel "dbConn.CreateStatement", dove dbConn contiene "DriverManager.GetConnection(zdbc:sqlite-3:/media/dirdati/dativari/contabfam/ContabFamdb_prove)"0: SQL Error: unable to open database file
Il contenuto di dbDati è corretto.   :o
Titolo: Re:leggere tabelle di DB SQLite3 con Zeos
Inserito da: Stilgar - Aprile 15, 2021, 08:53:34 am
Ciao.
Prova ad usare
Codice: [Seleziona]
const
  CurrentProtocol = 'sqlite';

Codice: [Seleziona]
  DriverManager.GetConnection(DriverManager.ConstructURL(CurrentProtocol, '', fileName, '', '', 0, nil));


Giusto per verifica.


Stilgar
Titolo: Re:leggere tabelle di DB SQLite3 con Zeos
Inserito da: petrusic - Aprile 15, 2021, 11:37:38 am
@ Stilgar
Ho provato a fare come mi hai detto:

Il messaggio d'Errore è cambiato, ma il risultato finale non cambia.
Citazione
Il progetto ContDomInDomusRatio ha sollevato una eccezione di classe 'External: SIGSEGV'.

 Nel file 'frmmain.pas' alla riga 158:
stmt := dbConn.CreateStatement;
riporto il codice modificato:
Codice: [Seleziona]
const
  CurrentProtocol = 'sqlite';
  dbCopia: String = '/media/dirdati/dativari/contabfam/ContabFamdb_prove'; 
  dbOrig: String = '/media/dirdati/dativari/contabfam/ContabFamdb';
var
  AProcess: TProcess;

  dbConn: IZConnection;
  stmt  :IZStatement;
  rs  :IZResultSet;
[...]
  dbDati: String = 'zdbc:sqlite:';

begin                                                         
[...]
 dbDati:= dbDati + dbCopia;
  DriverManager.GetConnection(DriverManager.ConstructURL(CurrentProtocol, '', dbDati, '', '', 0, nil));
 // DriverManager.GetConnection(DriverManager.ConstructURL(CurrentProtocol, '', dbCopia, '', '', 0, nil));

  If FindFirst(dirFogli + 'ContDom*.*', faAnyFile,  Esito)=0 then
  begin
    Label1.Caption:= 'Directory corrente:' + chr(10) + dirFogli;
    repeat
      begin
        lun:= Length(Esito.Name);
        annata:= Copy(Esito.Name,(lun-7),4);
        WriteLn('nomeFoglio= "' + Esito.Name + '" / annata: -' + annata + '-');
        dtIni:= annata + '0101';
        dtFin:= annata + '1231';

        stmt := dbConn.CreateStatement;                         
Ho riportato due righe per DriverManager.GetConnection, perchè non ho capito se la variabile da te indicata come "nomefile" doveva contenere sempre la prima parte ('zdbc:sqlite:'). In ogni caso, eseguendo il programma, la prima volta con la 2.a riga, la seconda volta, con la 1.a riga, l'Errore prodotto è sempre lo stesso.

Temo che dovrò abbandonare IZConnetion per ritornare a TZConnection, oppure, drasticamente, dovrò abbandonare addirittura ZEOS.
 :-\
Titolo: Re:leggere tabelle di DB SQLite3 con Zeos
Inserito da: DragoRosso - Aprile 15, 2021, 12:06:36 pm
Temo che dovrò abbandonare IZConnetion per ritornare a TZConnection, oppure, drasticamente, dovrò abbandonare addirittura ZEOS.

A mio parere, ritorna dal componente TZConnection. Poi pian piano, quando avrai voglia, nuovo entusiamo e tempo puoi ripartire a vedere come il componente funziona e come si interfaccia con l'IZConnection.
Io purtroppo non posso aiutarti perchè Lazarus l'ho preso in mano da poco.

Ciao, mai disperarsi, la soluzione si trova sempre.
Titolo: Re:leggere tabelle di DB SQLite3 con Zeos
Inserito da: petrusic - Aprile 15, 2021, 03:43:26 pm
A mio parere, ritorna dal componente TZConnection. Poi pian piano, quando avrai voglia, nuovo entusiamo e tempo puoi ripartire a vedere come il componente funziona e come si interfaccia con l'IZConnection.
Io purtroppo non posso aiutarti perchè Lazarus l'ho preso in mano da poco.

Se esistesse una guida unica e dettagliata di Lazarus e free pascal, sarebbe tutto molto più semplice e probabilmente potrei fare a meno di Zesolib. Infatti Lazarus ha i suoi strumenti per gestire database, ma, come molto materiale appartenente al mondo Linux, quello che si trova in giro è costituito molto spesso da guide scritte da volontari appassionati, ai quali va il ringrazìamento  più sincero e pieno di graditudine. Poi ci sono gli esempi e le esperienze di vita vissuta, all'interno dei forum. Ma questi ultimi, per lo più in lingua inglese, non sono quasi mai calzanti con lo spicchio di coidice che bisognerebbe mettere giù per i propri scopi.

Attenzione! La mia non è una critica, nè una lamentela, ma solamente un breve resoconto delle difficoltà logistiche che ho incontrato nel mio cammino, fino ad oggi.

Il mondo pascal in Lazarus mi piace tantissimo, ma per imparare quel poco che ho imparato è trascorso già un anno, mentre la mia vita scorre più velocemente. In ogni caso devo tantissimo agli amici di questo Forum, che ho trovato sempre disponibili a sostenermi, rispondendo alle mie sempre presenti difficoltà, tanto è che sono arrivato ai primi posti della hit parade dei messaggi scritti .  :-[

Per lavorare sui DB non so più cosa fare: Zeos mi ha un pò deluso, Ho provato gli strumenti propri di Lazarus, ma anch'essi sono risultati ostici e li ho abbandonati.
Si lo so, posso tornare a ZConnection, ma se voglio aggiornare, per es. 5 tabelle di DB, dando un solo Commit alla fine di tutti e 5 gli agli aggiornamentio, devo disegnare 5 ZQuery: dalla 1, alla 5 e quando tutto è pronto, finalmente, posso e devo comandare 5 Commit.
 :-\
Titolo: Re:leggere tabelle di DB SQLite3 con Zeos
Inserito da: DragoRosso - Aprile 15, 2021, 04:48:15 pm
Comprendo cosa vuoi dire, ma sinceramente non ritengo che usare 5 Query e fare 5 Commit sia un problema.
Magari non è quello a cui volevi arrivare, ma il tuo scopo lo hai raggiunto.

Se il creatore di Linux avesse atteso di avere il BlueTooth per pubblicare Linux, ancora ad oggi Linux non esisterebbe.

E se il problema è che una delle commit possa fallire andando a rendere incoerente il database, nelle risposte passate vedrai che ci hanno dato anche una soluzione con gli strumenti propri di SQLite (SAVEPOINT, ROLLBACK e RELEASE).

In quanto alla fatica di programmare, quello lo troverai sempre, e te lo dice uno che sicuramente non è da F1, ma che ha visto tanti linguaggi e sistemi operativi (molti dove il copia ed incolla era un miraggio), in cui molto spesso si programmava con il comando "ed" di Unix.

Quando si è alla prime armi (non come programmatore) con l'uso di un componente conviene usare ciò che viene messo a disposizione, poi si affina la tecnica.

Vai comunque tranuqillo che usare 5 query non è un problema.

Saluti

Titolo: Re:leggere tabelle di DB SQLite3 con Zeos
Inserito da: petrusic - Aprile 15, 2021, 04:57:53 pm
...  usare 5 query non è un problema.

Anche se non mi piace e se mi sembra funzionalmente illogico, concordo perfettamente.
Titolo: Re:leggere tabelle di DB SQLite3 con Zeos
Inserito da: Stilgar - Aprile 15, 2021, 10:19:48 pm
Il debugger lo usi?


Perchè ho l'impressione che tu stia mandando 2 volte il protocollo da usare (quindi il driver).
Magari mi sbaglio.


Immagino che tu abbia delle stringhe del tipo
Codice: [Seleziona]
  dbDati:= dbDati + dbCopia;
  DriverManager.GetConnection(DriverManager.ConstructURL(CurrentProtocol, '', dbDati, '', '', 0, nil));
  // dbDati('zdbc:sqlite:')+dbCopia('/media/dirdati/dativari/contabfam/ContabFamdb_prove')


Quindi l'url risultante è quasi certamente sbagliato.


Qualcosa del tipo 'zdbc:sqlite:zdbc:sqlite:/il tuo file'
Carica l'url in una variabile e controlla col debugger il suo contenuto. O stampa a console il valore, come ti trovi meglio.
Stilgar




Titolo: Re:leggere tabelle di DB SQLite3 con Zeos
Inserito da: Stilgar - Aprile 15, 2021, 10:59:13 pm
Ti lascio un esempio di un metodo che ho scritto, qui creo tabelle al volo dentro sqlite.


Codice: [Seleziona]

procedure TDatabaseModule.createStructures(fileName: TFileName);
begin
  try
    FUpdateConnection := DriverManager.GetConnection(DriverManager.ConstructURL(CurrentProtocol, '', fileName, '', '', 0, nil));
    try
      if not tablePresent(FUpdateConnection, 'Subject') then
      begin
        FUpdateConnection.CreateStatement.Execute('CREATE TABLE Subject (id VARCHAR(37) primary key, title varchar(50))');
      end;
      if not tablePresent(FUpdateConnection, 'Note') then
      begin
        FUpdateConnection.CreateStatement.Execute('CREATE TABLE Note (id VARCHAR(37) primary key, subjectId VARCHAR(37), title varchar(100), [text] text, password varchar(200), FOREIGN KEY(subjectId) REFERENCES Subject(id))');
      end;
      if not tablePresent(FUpdateConnection, 'Attach') then
      begin
        FUpdateConnection.CreateStatement.Execute('CREATE TABLE Attach (id VARCHAR(37) primary key, noteId VARCHAR(37), comment varchar(2000))');
      end;
      if not tablePresent(FUpdateConnection, 'ToDo') then
      begin
        FUpdateConnection.CreateStatement.Execute('CREATE TABLE ToDo (id VARCHAR(37) primary key, noteId VARCHAR(37), comment varchar(2000), done int default 0)');
      end;
      if not tablePresent(FUpdateConnection, 'Info') then
      begin
        FUpdateConnection.CreateStatement.Execute('CREATE TABLE Info (key varchar(30) primary key, value varchar(100));');
        FUpdateConnection.CreateStatement.Execute('INSERT INTO Info (key,value)values(''format-version'',''2.0'')');
      end;
      FUpdateConnection.CreateStatement.Execute('VACUUM');
    finally
      FUpdateConnection.Close;
    end;
  except
    on e: Exception do
    begin
      writeln(e.Message);
    end;
  end;
  FUpdateConnection := nil;
end;

Codice: [Seleziona]

function TDatabaseModule.tablePresent(datasource: IZConnection; tableName: string): boolean;
var
  rs: IZResultSet;
begin
  Result := False;
  rs := datasource.GetMetadata.GetTables('', '', '%' + tableName + '%', ['TABLE']);
  while rs.Next do
  begin
    tableName := rs.GetStringByName('TABLE_NAME');
    if tableName = tableName then
    begin
      Result := True;
      break;
    end;
  end;
end;
Stilgar
Titolo: Re:leggere tabelle di DB SQLite3 con Zeos
Inserito da: petrusic - Aprile 16, 2021, 10:20:20 am
Il debugger lo usi?
[...]
Perchè ho l'impressione che tu stia mandando 2 volte il protocollo da usare (quindi il driver).
[...]
Carica l'url in una variabile e controlla col debugger il suo contenuto. O stampa a console il valore, come ti trovi meglio.
Il debugger lo uso, ma per constatare insieme il contenuto dell'URL faccio così
Codice: [Seleziona]
  dbDati:= dbDati + dbCopia;
  striMia:= DriverManager.ConstructURL(CurrentProtocol, '', dbDati, '', '', 0, nil);
  Writeln('DriverManager.URL= "' + striMia + '"');
  DriverManager.GetConnection(DriverManager.ConstructURL(CurrentProtocol, '', dbDati, '', '', 0, nil));
Ecco il risultato
Citazione
DriverManager.URL= "zdbc:sqlite:///zdbc:sqlite:/media/dirdati/dativari/contabfam/ContabFamdb_prove"

Sostituendo dbDati con dbCopia, ottengo:
Citazione
DriverManager.URL= "zdbc:sqlite:////media/dirdati/dativari/contabfam/ContabFamdb_prove"
Non capisco la presenza delle "///" precedenti il percorso contenuto nella variabile dbCopia (dalla finestra del debugger: "dbCopia = '/media/dirdati/dativari/contabfam/ContabFamdb_prove'
const
 dbCopia:
String
/media/dirdati/dativari/lazarus_progetti/lazarus_progetti_miei/daContDom_aDomusRatio/frmmain.pas(96,3))




Titolo: Re:leggere tabelle di DB SQLite3 con Zeos
Inserito da: Stilgar - Aprile 16, 2021, 01:44:16 pm
Ciao.
Il fatto che ci siano 3 / dipende dal fatto che si sta lavorando con un url.


Se leggi una pagina html da disco il browser visualizzerà nella barra degli indirizzi una cosa simile a "file:///"


URL,URI e URN hanno una loro sintassi.




Cmq, ora ti si apre quel benedetto file?




Stilgar.
Titolo: Re:leggere tabelle di DB SQLite3 con Zeos
Inserito da: petrusic - Aprile 16, 2021, 05:08:09 pm
Cmq, ora ti si apre quel benedetto file?
Si, finalmente si apre.

Le istruzioni finali usate fino ad ora ("fino ad ora", perchè non ho ancora approfondito sugli esempi che mi hai riportato prima.)
sono:
Codice: [Seleziona]
const
  CurrentProtocol = 'sqlite';

  dbCopia: String = '/media/dirdati/dativari/contabfam/ContabFamdb_prove';
  dbOrig: String = '/media/dirdati/dativari/contabfam/ContabFamdb';
var
  FConn: IZConnection;
  stmt  :IZStatement;
  rs  :IZResultSet;                           
[...]
begin
  striMia:= DriverManager.ConstructURL(CurrentProtocol, '', dbCopia, '', '', 0, nil);
  Writeln('DriverManager.URL= "' + striMia + '"');
  FConn:= DriverManager.GetConnection(striMia);                   
[...]
       stmt := FConn.CreateStatement;
        sql:= 'SELECT DtCoMovg,StaDtMovg FROM riepmovg WHERE DtCoMovg >= ' + dtIni + ' AND DtCoMovg <= ' + dtFin;
        rs   := stmt.ExecuteQuery(sql);
        totRecQry:= rs.GetRow;     // totRecQry contiene sempre 0, anche quando il range di ricerca non è nullo
[...]
Devo approfondire l'indagune di ricerca errori nelle ultime due righe del mio codice.
 :D
Titolo: Re:leggere tabelle di DB SQLite3 con Zeos
Inserito da: Stilgar - Aprile 16, 2021, 05:39:20 pm
GetRow dovrebbe (ma non ricordo bene) a che posizione sei nel cursore.


Quindi senza while rs.next do dovrebbe restare sempre a zero.


Stilgar
Titolo: Re:leggere tabelle di DB SQLite3 con Zeos
Inserito da: petrusic - Aprile 16, 2021, 06:57:33 pm
GetRow dovrebbe (ma non ricordo bene) a che posizione sei nel cursore.
Non capisco bene cosa vuoi dire. Vuoi sapere dove si trova la "G" di GetRow ?  La risposta è: posiz.26  ::)

Quindi senza while rs.next do dovrebbe restare sempre a zero.
Hai ragione a fare quella riflessione, il codice è sparuto, ma ho riportato solo l'essenziale. Prima di questo momento riportarlo tutto non aveva senso.
Le istruzioni di lettura della tabella di DB (riepmovg) sono all'interno di un ciclo iterativo comandato dalla fine dei file .odt contenenti i fogli di calcolo da trattare:
Codice: [Seleziona]
procedure TForm1.FormCreate(Sender: TObject);
const
  dirFogli: string = '/media/dirdati/dativari/ContDom/';
  CurrentProtocol = 'sqlite';

  dbCopia: String = '/media/dirdati/dativari/contabfam/ContabFamdb_prove';
  dbOrig: String = '/media/dirdati/dativari/contabfam/ContabFamdb';
var
  Esito: TSearchRec;

  FConn: IZConnection;
  stmt  :IZStatement;
  rs  :IZResultSet;
  swAster: Boolean = False;

  lun, totRecQry: Integer;

  annata, dtIni, dtFin, sql, striMia: String;
begin
//---------------------------------------------------- Salvataggio prudenziale del DB "ContabFamdb" -------------------------------------------------
  CopyFile(dbOrig, dbCopia);
  ShowMessage('"ContabFamdb" copiato in "ContabFamdb_prove"');
//---------------------------------------------------- FINE salvataggoio ---------------------------------------------------------------------------------
  striMia:= DriverManager.ConstructURL(CurrentProtocol, '', dbCopia, '', '', 0, nil);
  Writeln('DriverManager.URL= "' + striMia + '"');
  FConn:= DriverManager.GetConnection(striMia);
  stmt:= FConn.CreateStatement;
  If FindFirst(dirFogli + 'ContDom*.*', faAnyFile,  Esito)=0 then
  begin
    Label1.Caption:= 'Directory corrente:' + chr(10) + dirFogli;
    repeat
      begin
        lun:= Length(Esito.Name);
        annata:= Copy(Esito.Name,(lun-7),4);
        WriteLn('nomeFoglio= "' + Esito.Name + '" / annata: -' + annata + '-');
//        dtIni:= annata + '0101';
//        dtFin:= annata + '1231';
dtIni:= '20020101';   // istruzione di prova di inizio periodo con 352 record
dtFin:= '20021231';   // istruzione di prova di fine periodo con 352 record
        sql:= 'SELECT DtCoMovg,StaDtMovg FROM riepmovg WHERE DtCoMovg >= ' + dtIni + ' AND DtCoMovg <= ' + dtFin;
        try
          rs:= stmt.ExecuteQuery(sql);
          totRecQry:= rs.GetRow;
        except
          on e: Exception do
          begin
            writeln(e.Message);
          end;
        end;
          FConn.Close;
          FConn:= nil;
          case totRecQry of
          0:
           begin
            striMia:= '-' + Esito.Name;
            lsDir.Items.Add(striMia);
           end
           else
             begin;
              WriteLn('*** TROVATI in riepmovg, per l''annata -'+ annata + '- n.' + IntToStr(totRecQry) + '***');
              striMia:= '*' + Esito.Name;
              swAster:= True;
              lsDir.Items.Add(striMia);
             end;
        end;
      end;
    Until FindnNext(Esito) <> 0;
    FindClose(Esito);
//   Writeln('Lettura directory -> TERMINATA' );
    PulsAvvia.Caption:='AVVIO' + chr(10) + 'LAVORO';
    case
      swAster of
        True:
        begin
          leInfoMov.Visible:= True;
        end;
    end;
  end;
end;
Ora ho riportato tutta la procedura. Ho inserito una data di inizio e fine periodo dove esistono 352 record (verificato col DBBrowser di SQLite), proprio per verificare il funzionamento della query scritta. Infatti la tengo sotto controllo in debugger e la faccio eseguire una volta sola.
Purtroppo non ho capito cosa manca per farla funzionare.
Titolo: Re:leggere tabelle di DB SQLite3 con Zeos
Inserito da: Stilgar - Aprile 16, 2021, 09:33:20 pm
Scusa.
Ho scritto senza rileggere.
Getrow dovrebbe restituire la posizione del cursore.

Non spostando il cursore sei alla riga zero.
Come scrivevo nella miniguida, serve un rs.next
Se restituisce false vuol dire che non c'è più nulla nel cursore.

Stilgar
Titolo: Re:leggere tabelle di DB SQLite3 con Zeos
Inserito da: petrusic - Aprile 16, 2021, 10:46:16 pm
Getrow dovrebbe restituire la posizione del cursore.

Non spostando il cursore sei alla riga zero.
Come scrivevo nella miniguida, serve un rs.next
Se restituisce false vuol dire che non c'è più nulla nel cursore.
Ah! Avevo capito male. Secondo me "rs.GetRow" era l'istruzione corrispondente alla "Form1.ZQuery1.RecordCount" di TZConnection.
Anche, leggendo la miniguida, non avevo capito che bisognava attivare un contatore di righe artigianale.

Comunque, anche questa è andata ed ho potuto fare, grazie alla tua superilluminazione, un altro piccolo passo avanti.
Titolo: Re:leggere tabelle di DB SQLite3 con Zeos
Inserito da: petrusic - Aprile 20, 2021, 12:16:39 pm
Mi riferisco al seguente passo della miniguida Zeos dietro le quinte (http://www.lazaruspascal.it/index.php?page=111):
Citazione
Come si accede alle colonne?
cursor.get[xxxxx](indice).

Poichè devo risalire all'ultimo progressivo ID di ciascuna tabella del DB, (l'ID occupa sempre la colonna 1), ho provato a scrivere una function unica in grado di leggere il contenuto della prima colonna dell'ultima riga di una tabella qualsiasi:
Codice: [Seleziona]
function LeggiUltimoID(nmTabDB: String):Integer;
const
CurrentProtocol = 'sqlite';

var
  FConn: IZConnection;
  stmt: IZStatement;
  rs: IZResultSet;

  numID, sql, striMia: String;

begin
  striMia:= DriverManager.ConstructURL(CurrentProtocol, '', dbCorr, '', '', 0, nil);
  FConn:= DriverManager.GetConnection(striMia);
  stmt:= FConn.CreateStatement;
   sql:= 'SELECT fields[1] as col1 FROM ' + nmTabDB + 'ORDER BY col1 DESC LIMIT 1';
  rs:= stmt.ExecuteQuery(sql);
  numID:= LoadCursorFromLazarusResource(col1);    // dovrebbe leggere il contenuto della colonna 1 della tabella corrente del DB

end;
Ebbene, numID assume 0(zero).
Citazione
numID = 0
var
 numID: Integer = longint
/media/dirdati/dativari/lazarus_progetti/lazarus_progetti_miei/daContDom_aDomusRatio/frmmain.pas(1216,3)
Titolo: Re:leggere tabelle di DB SQLite3 con Zeos
Inserito da: DragoRosso - Aprile 20, 2021, 02:47:12 pm
Codice: [Seleziona]
 numID:= LoadCursorFromLazarusResource(col1);    // dovrebbe leggere il contenuto della colonna 1 della tabella corrente del DB

Non penso che quello ti ritorni il valore contenuto nel campo 'col1', ha a che fare con tuttaltro.

Tramite Interfaccia non conosco la funzione da chiamare, ma penso che sia tramite

Codice: [Seleziona]
rs.getRow
Titolo: Re:leggere tabelle di DB SQLite3 con Zeos
Inserito da: petrusic - Aprile 20, 2021, 03:26:47 pm
Codice: [Seleziona]
 numID:= LoadCursorFromLazarusResource(col1);    // dovrebbe leggere il contenuto della colonna 1 della tabella corrente del DB

Non penso che quello ti ritorni il valore contenuto nel campo 'col1', ha a che fare con tuttaltro.

Tramite Interfaccia non conosco la funzione da chiamare, ma penso che sia tramite

Codice: [Seleziona]
rs.getRow
Convengo con te che "LoadCursorFromLazarusResource(col1)" non dia il risultato desiderato, ma il comando "cursor.get[xxxxx](indice)", che ho trovato nella miniguida di Stilgar non viene riconosciuto, perciò non ho potuto portare a termine la prova. Quel Load... li, effettivamente non nha funzionato, ma mi ha permesso di terminare la prova. Il mio problema è che non riesco a trovare la sintassi corretta per il mio scopo, nè riesco ad immaginarmela.

il comando "rs.getRow", come mi ha attenzionato Stilgar qui (http://www.lazaruspascal.it/index.php?topic=2449.msg15611#msg15611), fornisce solamente un contatore inizializzato a 0(zero), pronto per essere incrementato attraverso un ciclo del tipo "while rs.next do ".

Titolo: Re:leggere tabelle di DB SQLite3 con Zeos
Inserito da: Stilgar - Aprile 20, 2021, 09:05:09 pm
Ciao.
Stiamo parlando di software open source in fin dei conti.
 Ctrl+Click e si aprono i sorgenti  ;D  Non serve la guida.
Codice: [Seleziona]
    function IsNull(ColumnIndex: Integer): Boolean;
    function GetPChar(ColumnIndex: Integer): PChar;
    function GetPAnsiChar(ColumnIndex: Integer): PAnsiChar; overload;
    function GetPAnsiChar(ColumnIndex: Integer; out Len: NativeUInt): PAnsiChar; overload;
    function GetString(ColumnIndex: Integer): String;
    {$IFNDEF NO_ANSISTRING}
    function GetAnsiString(ColumnIndex: Integer): AnsiString;
    {$ENDIF}
    {$IFNDEF NO_UTF8STRING}
    function GetUTF8String(ColumnIndex: Integer): UTF8String;
    {$ENDIF}
    function GetRawByteString(ColumnIndex: Integer): RawByteString;
    function GetBinaryString(ColumnIndex: Integer): RawByteString; deprecated;
    function GetUnicodeString(ColumnIndex: Integer): ZWideString;
    function GetPWideChar(ColumnIndex: Integer): PWideChar; overload;
    function GetPWideChar(ColumnIndex: Integer; out Len: NativeUInt): PWideChar; overload;
    function GetBoolean(ColumnIndex: Integer): Boolean;
    function GetByte(ColumnIndex: Integer): Byte;
    function GetShort(ColumnIndex: Integer): ShortInt;
    function GetWord(ColumnIndex: Integer): Word;
    function GetSmall(ColumnIndex: Integer): SmallInt;
    function GetUInt(ColumnIndex: Integer): Cardinal;
    function GetInt(ColumnIndex: Integer): Integer;
    function GetULong(ColumnIndex: Integer): UInt64;
    function GetLong(ColumnIndex: Integer): Int64;
    function GetFloat(ColumnIndex: Integer): Single;
    function GetDouble(ColumnIndex: Integer): Double;
    function GetCurrency(ColumnIndex: Integer): Currency;
    function GetBigDecimal(ColumnIndex: Integer): Extended;
    function GetBytes(ColumnIndex: Integer): TBytes;
    function GetDate(ColumnIndex: Integer): TDateTime;
    function GetTime(ColumnIndex: Integer): TDateTime;
    function GetTimestamp(ColumnIndex: Integer): TDateTime;
    function GetAsciiStream(ColumnIndex: Integer): TStream;
    function GetUnicodeStream(ColumnIndex: Integer): TStream;
    function GetBinaryStream(ColumnIndex: Integer): TStream;
    function GetBlob(ColumnIndex: Integer): IZBlob;
    function GetDataSet(ColumnIndex: Integer): IZDataSet;
    function GetValue(ColumnIndex: Integer): TZVariant;
    function GetDefaultExpression(ColumnIndex: Integer): string;
    //======================================================================
    // Methods for accessing results by column name
    //======================================================================
    function IsNullByName(const ColumnName: string): Boolean;
    function GetPCharByName(const ColumnName: string): PChar;
    function GetPAnsiCharByName(const ColumnName: string): PAnsiChar; overload;
    function GetPAnsiCharByName(const ColumnName: string; out Len: NativeUInt): PAnsiChar; overload;
    function GetStringByName(const ColumnName: string): String;
    {$IFNDEF NO_ANSISTRING}
    function GetAnsiStringByName(const ColumnName: string): AnsiString;
    {$ENDIF}
    {$IFNDEF NO_UTF8STRING}
    function GetUTF8StringByName(const ColumnName: string): UTF8String;
    {$ENDIF}
    function GetRawByteStringByName(const ColumnName: string): RawByteString;
    function GetBinaryStringByName(const ColumnName: string): RawByteString; deprecated;
    function GetUnicodeStringByName(const ColumnName: string): ZWideString;
    function GetPWideCharByName(const ColumnName: string): PWideChar; overload;
    function GetPWideCharByName(const ColumnName: string; out Len: NativeUInt): PWideChar; overload;
    function GetBooleanByName(const ColumnName: string): Boolean;
    function GetByteByName(const ColumnName: string): Byte;
    function GetShortByName(const ColumnName: string): ShortInt;
    function GetWordByName(const ColumnName: string): Word;
    function GetSmallByName(const ColumnName: string): SmallInt;
    function GetUIntByName(const ColumnName: string): Cardinal;
    function GetIntByName(const ColumnName: string): Integer;
    function GetULongByName(const ColumnName: string): UInt64;
    function GetLongByName(const ColumnName: string): Int64;
    function GetFloatByName(const ColumnName: string): Single;
    function GetDoubleByName(const ColumnName: string): Double;
    function GetCurrencyByName(const ColumnName: string): Currency;
    function GetBigDecimalByName(const ColumnName: string): Extended;
    function GetBytesByName(const ColumnName: string): TBytes;
    function GetDateByName(const ColumnName: string): TDateTime;
    function GetTimeByName(const ColumnName: string): TDateTime;
    function GetTimestampByName(const ColumnName: string): TDateTime;
    function GetAsciiStreamByName(const ColumnName: string): TStream;
    function GetUnicodeStreamByName(const ColumnName: string): TStream;
    function GetBinaryStreamByName(const ColumnName: string): TStream;
    function GetBlobByName(const ColumnName: string): IZBlob;
    function GetDataSetByName(const ColumnName: String): IZDataSet;
    function GetValueByName(const ColumnName: string): TZVariant;


    //=====================================================================
    // Advanced features:
    //=====================================================================


    function GetWarnings: EZSQLWarning;
    procedure ClearWarnings;


    function GetCursorName: String;
    function GetMetadata: IZResultSetMetadata;
    function FindColumn(const ColumnName: string): Integer;


Ho provato ad eseguire la query:
Codice: [Seleziona]
20/04/2021 20:45:25: SQL Error: near "as": syntax error  <SELECT fields[1] as col1 FROM Note ORDER BY col1 DESC LIMIT 1>
20/04/2021 20:45:33: SQL Error: no such column: fields  <SELECT fields[1] FROM Note ORDER BY col1 DESC LIMIT 1>

La query da lanciare (se ho capito quello che vuoi fare) è :
Codice: [Seleziona]
SELECT max(RowId) FROM laTuaTabella

(Sempre che tu non abbia creato la tabella in modo che il rowid non venga generato, non serve nemmeno la sequance con l'autoincrement per puntare alla riga).



Se stai estraendo un intero dalla prima colonna:
Codice: [Seleziona]
rs.getLong(1);
o
rs.getInt(1);


Stilgar
Titolo: Re:leggere tabelle di DB SQLite3 con Zeos
Inserito da: petrusic - Aprile 20, 2021, 10:40:15 pm
@ Stilgar

A fatica, ma ho capito:
Codice: [Seleziona]
  sql:= 'SELECT max(RowId) FROM ' + nmTabDB;
  rs:= stmt.ExecuteQuery(sql);
  numID:= rs.GetInt(1);    // legge il contenuto della colonna ID(col.1) dell'ultima riga della tabella corrente del DB
  Result:= numID;                 

Risultato:
Citazione
numID = 59936
var
 numID: Integer = longint
/media/dirdati/dativari/lazarus_progetti/lazarus_progetti_miei/daContDom_aDomusRatio/frmmain.pas(1187,3)
L'ho verificato col DB Browser: --> ("SELECT max(RowId) FROM movimgg" = 59936)
 :D :D :D
Titolo: Re:leggere tabelle di DB SQLite3 con Zeos
Inserito da: moessner - Giugno 13, 2021, 01:54:51 pm
Dimenticavo,
 
Uso componenti ZEOS LIB

ed ho definito il protocollo=sqlite-3   nel Zconnection

Grazie ancora
Moessenr
Titolo: Re:leggere tabelle di DB SQLite3 con Zeos
Inserito da: petrusic - Agosto 29, 2021, 07:12:37 pm
Riprendo questa mia vecchia discussione perchè, andando avanti nell'apprendimento di come accedere ad un DB e trattare le tabelle in esso contenute, ho rivisto i comandi adoperati recentemente in un programma per la la Open del DB:
Codice: [Seleziona]
...
var
 FConn: IZConnection;
  stmt  :IZStatement;
  rs  :IZResultSet;   
...
  striMia:= DriverManager.ConstructURL(CurrentProtocol, '', '/media/dirdati/dativari/contabfam/Fragola', '', '', 0, nil);     // Il DB Fragola non esiste ma vorrei che me lo dicesse il programma

  FConn:= DriverManager.GetConnection(striMia);  // prepara l’accesso al DB, cercandolo
try
  stmt:= FConn.CreateStatement;  // eseguela Open del DB
  except
    on e: Exception do
    begin
          ShowMessage('DB NON trovato');
    end;
  end;                                                   
Speravo che venisse sollevata un'eccezione, invece non è successo niente.
Sicuramente ho sbagliato io, ma non capisco dove.

Pensavo di poterl
Titolo: Re:leggere tabelle di DB SQLite3 con Zeos
Inserito da: DragoRosso - Agosto 29, 2021, 09:27:41 pm
Non ha ancora usato ZeosDB in maniera completa.
Però penso che l'apertura di una connessione non generi eccezioni: forse le genereranno le istruzioni successive (come ad esempio il tentativo di apertura di una tabella).

Tramite la connessione, ritengo che si possano aggiungere tabelle, creare nuovi campi, etc ..., quindi la connessione esiste anche in un db "inesistente" ossia vuoto da "costruire".

Magari mi sbaglio, lascio il chiarimento ai professionisti del DB.

Ciao
Titolo: Re:leggere tabelle di DB SQLite3 con Zeos
Inserito da: petrusic - Agosto 29, 2021, 10:50:16 pm
Non ha ancora usato ZeosDB in maniera completa.
Però penso che l'apertura di una connessione non generi eccezioni:
Si, è così. Ho provato in debug ed ho visto chiaramente che l'eccezione non viene generata.

Ho modificato il passo di istruzioni così:
Codice: [Seleziona]
 striMia:= DriverManager.ConstructURL(CurrentProtocol, '', '/media/dirdati/dativari/contabfam/Fragola', '', '', 0, nil);
  try
  FConn:= DriverManager.GetConnection(striMia);  // prepara l’accesso al DB, cercandolo
  except
    on e: Exception do
    begin
      ShowMessage('DB NON trovato');
    end;
  end;
  stmt:= FConn.CreateStatement;  // eseguela Open del DB
//-----------
  sql:= 'SELECT DtCoMovg FROM riepmovg';
  try
  rs:= stmt.ExecuteQuery(sql);
  totRecQry:= rs.GetRow;    // "totquery" viene impostato a 0(puntamento primo record della query contenuta in sql)
  while rs.next do
  begin
    totRecQry:= totRecQry + 1;
  end;
  except
     on e: Exception do
     begin
         writeln(e.Message);
     end;
  end;
  ShowMessage('record letti in "riepmovg"= ' + IntToStr(totRecQry)); 
Anche così, al primo try l'eccezione non viene generata, mentre viene generata quando il programma tenta di leggere una tabella del DB.
A quel punto però è troppo tardi per capire che l'Errore risale all'inesistenza del DB, sia perchè il momento è diverso, sia perchè il messaggio ricavato nell'except a me non risulta chiaro:



Titolo: Re:leggere tabelle di DB SQLite3 con Zeos
Inserito da: petrusic - Agosto 29, 2021, 11:07:48 pm
Ho modificato ulteriormente il passo di programma:
Codice: [Seleziona]
 striMia:= DriverManager.ConstructURL(CurrentProtocol, '', '/media/dirdati/dativari/contabfam/Fragola', '', '', 0, nil);
  try
  FConn:= DriverManager.GetConnection(striMia);  // prepara l’accesso al DB, cercandolo
  except
    on e: Exception do
    begin
      ShowMessage('DB NON trovato');
    end;
  end;
  stmt:= FConn.CreateStatement;  // eseguela Open del DB
//-----------
  sql:= 'SELECT DtCoMovg FROM riepmovg';
  try
  rs:= stmt.ExecuteQuery(sql);
  except
     on e: Exception do
     begin
         writeln(e.Message);
     end;
  end;
  totRecQry:= rs.GetRow;    // "totquery" viene impostato a 0(puntamento primo record della query contenuta in sql)
  try
  while rs.next do
  begin
    totRecQry:= totRecQry + 1;
  end;
  except
     on e: Exception do
     begin
         writeln(e.Message);
     end;
  end;
  ShowMessage('record letti in "riepmovg"= ' + IntToStr(totRecQry));               

Questa volta il messaggio relativo al sollevamento dell'eccezione risulta un pò più chiaro, in quanto si capisce dove viene sollevata l'eccezione. In ogni caso non si capisce perchè, infatti il fatto che il n° di record letti sia 0, può essere anche dovuto al fatto che la tabella, pur esistendo, non sia stata ancora riempita.
Titolo: Re:leggere tabelle di DB SQLite3 con Zeos
Inserito da: bonmario - Agosto 30, 2021, 07:56:59 am
Non conosco Zeos, preferisco usare ciò che mette già a disposizione Lazarus, se però non dovessi trovare altre strade, puoi sempre fare un controllo preventivo ed emettere un errore se il DB non esiste, usando la FileExist, in caso contrario, procedi con la connessione.

Ciao, Mario
Titolo: Re:leggere tabelle di DB SQLite3 con Zeos
Inserito da: DragoRosso - Agosto 30, 2021, 09:48:28 am
Io normalmente per i database faccio così:

- Verifico che il file database esista (*);

- Ho sempre una tabella VER: mantiene la versione del database (legata al mio prodotto ovviamente) (*);

- Se la tabella VER non esiste il database è vuoto (*);

- Se il primo record di VER ha un contenuto diverso da quello che mi aspetto, allora eseguo una ALTER TABLE su tutte le tabelle del database per adeguare i campi.

(*) Queste eventualità adesso mi generano un messaggio utente, solo in un vecchissimo progetto creai delle tabelle tramite SQL a codice.

Devo vedere come testatavo le varie condizioni, e poi come riportarle in ZEOS.

Ciao
Titolo: Re:leggere tabelle di DB SQLite3 con Zeos
Inserito da: petrusic - Agosto 30, 2021, 09:59:19 am
puoi sempre fare un controllo preventivo ed emettere un errore se il DB non esiste, usando la FileExist, in caso contrario, procedi con la connessione.
Ho accolto piacevolmente il tuo consiglio, perchè era proprio quello che avevo pensato di fare stamani, prima d' accendere il pc.

@ DragoRosso:
Grazie per il tuo suggerimento. Tuttavia, io credo che, utilizzando SQLite3, in cui il DB risiede in un unico File, controllare anche l'esistenza delle singole tabelle, sia, a mio avviso, uno sforzo che posso risparmiarmi, daltronde io non scrivo programmi destinati all'uso di terzi.
Verificare la presenza di record all'interno delle tabelle è effettivamente un'altra cosa. Ci penso un pò su.
Grazie.
Titolo: Re:leggere tabelle di DB SQLite3 con Zeos
Inserito da: DragoRosso - Agosto 30, 2021, 10:11:19 am
QUESTO è il codice usato con il cugino più quotato di Lazarus con SQLite.
Penso sia facilmente adattabile in ZEOS (se non è addirittura già compatibile).

Il codice è incompleto, alcune variabili potrebbero essere non dichiarate in questa funzione, ma il concetto dovrebbe essere chiaro.

Le istruzioni SQL CREATE TABLE e consecutive sono copiate pari pari dai tool di generazione dei database di terze parti (tipo SQLiteStudio).

Scusate la forma ma è codice vecchio di oltre quindici anni.

Codice: [Seleziona]
function TFProgVision.DB_Check(const sNomeDB: WideString; var sErrore: WideString): LongWord;
var tempelenco: TStringList;
    tempflag: boolean;
    VersioneDB: string;
    tempstr: string;
    i: Integer;
    tempResult: integer;
const
    tempDBList: array [0..6] of string = ('VisSubPassi', 'VisVerificaColore', 'VisMediaBinaria', 'VisConteggioFronti', 'VisFeltrini', 'VisSequenze', 'VisOCV');
    tempDBStepList: array [0..17] of string = ('DataOraUTC', 'Prg', 'SX', 'SubPasso', 'EsitoPasso', 'TipoControllo', 'ColoreControllo', 'ColoreLimiteLow', 'ColoreLimiteHigh', 'ScattaFoto', 'Exp1', 'Exp2', 'Exp3', 'Exp4', 'Exp5', 'Exp6', 'Exp7', 'Exp8');
    zero: integer = 0;
type
    tempDBtipo = (VisSubPassi=0, VisVerificaColore=1, VisMediaBinaria=2, VisConteggioFronti=3, VisFeltrini=4, VisSequenze=5, VisOCV=6);
begin
  result := ord(App_NoErrore);
  sErrore := ErroreDesc[ord(App_NoErrore)];
  //Questa funzione, quando viene chiamata attiva il sistema di reportistica tramite DB.
  //Inoltre effettua tutti i controlli e aggiorna automaticamente le tabelle della visione con la nuova struttura ove necessario.
  //Test della presenza del database
  try
    ForceDirectories(ExtractFilePath(sNomeDB));
    //Crea il buffer per gli Step (1 SubPasso solo di lunghezza fissa)
    //(DataOraUTC, Prg, SX, SubPasso, EsitoPasso, TipoControllo, ColoreControllo, "ColoreLimite(intensita)", ScattaFoto, Exp1, Exp2, Exp3, Exp4, Exp5, Exp6, Exp7, Exp8)
    if FDConnection1.Connected then
      begin
        FDConnection1.Close;
      end;
    VersioneDB := '50';
    tempelenco := TStringList.Create;
    //Non serve specificare in realtà la creazione, in quanto se non esiste il database viene creato qualunque modo si imposti.
    if FileExists(sNomeDB) then
      begin
        FDConnection1.Params.Values['OpenMode'] := 'omReadWrite';
      end
    else
      begin
        FDConnection1.Params.Values['OpenMode'] := 'omCreateUTF16';
      end;
    FDConnection1.Params.Database  := sNomeDB;
    FDConnection1.Open;
    //Se siamo arrivati sino a qui, vuole dire che il database comunqueè stato creato.
    //Controlliamo ogni singola tabella della visione
    //*
    //Qui viene creata la tabella Versione. Con la seguente istruzione andiamo a cearla se non esiste:
    FDConnection1.ExecSQL('create table if not exists Versione (Visione varchar(16))');
    //Ora controlliamo se c'è il campo "Visione", se non c'è lo creiamo.
    //Questo è il metodo fornito da SQLITE ('PRAGMA table_info(Versione)') per elencare i campi di una tabella;
    //quello applicato è invece più generico e funziona con tutti i database
    tempelenco.Clear;
    tempflag := false;
    i := 0;
    FDConnection1.GetFieldNames('', '', 'Versione', '', tempelenco);
    while i <  tempelenco.Count do
      begin
        if UpperCase(tempelenco[i]) = 'VISIONE' then
          begin
            tempflag := true;
            break;
          end;
        inc(i);
      end;
    if not tempflag then
      begin
        //Eseguiamo la AlterTable per aggiungere la colonna "Visione";
        FDConnection1.ExecSQL('alter table Versione ADD COLUMN Visione varchar(16)');
      end;
    //Leggiamo il valore della versione, e se differisce allora è necessario modificare il database (o tabelle o campi).
    //Se è Nullo lo aggiorniamo noi.
    FDQuery1.Active := false;
    FDQuery1.SQL.Clear;
    FDQuery1.SQL.Add('select Visione from Versione');
    FDQuery1.Active := true;
    FDQuery1.First;
    if FDQuery1.FieldByName('Visione').Value <> NULL then
      begin
        tempstr := FDQuery1.FieldByName('Visione').Value;
      end
    else
      begin
        tempstr := '50'; //per default viene inserito un valore di versione minore alla prima versione con il DB
        FDQuery1.Edit;
        FDQuery1.FieldByName('Visione').Value := tempstr;
        FDQuery1.Post;
      end;
    //Verifichiamo la versione del database (prime due cifre della versione del programma)
    if VersioneDB.ToInteger > tempstr.ToInteger  then
      begin
        //Dobbiamo aggiornare il database, ricreando le tabelle e trasferendo i vecchi dati
        //Vengono aggiornate tutte le tabelle, onde evitare qualsiasi problema
        //La procedura è identica a quella usata dai vari SQLITE Manager
        tempelenco.Clear;
        tempflag := false;
        i := 0;
        FDConnection1.GetTableNames( '', '', '', tempelenco);
        for i := Low(tempDBList) to High(tempDBList) do
          begin
            case tempDBtipo(i) of
              VisSubPassi:
                begin
                  //Per ogni tabella elencata, cerchiamose esiste già nel database
                  if tempelenco.IndexOf(tempDBList[i]) >=0 then
                    begin
                      //Trasferiamo i dati della tabella esistente e creiamo quella nuova, ritrasferendo tutti i dati.
                      FDConnection1.ExecSQL('PRAGMA foreign_keys = 0');
                      FDConnection1.ExecSQL('CREATE TABLE temp_'+tempDBList[i]+' AS SELECT * FROM '+tempDBList[i]);
                      FDConnection1.ExecSQL('DROP TABLE '+tempDBList[i]);
                      //Creiamo la nuova tabella
                      tempstr := 'CREATE TABLE if not exists '+tempDBList[i]+' (DataOraUTC DATETIME NOT NULL, Prg INTEGER NOT NULL, SX BOOLEAN NOT NULL, SubPasso INTEGER NOT NULL, EsitoPasso BOOLEAN NOT NULL, ';
                      tempstr := tempstr + 'TipoControllo varchar(16) NOT NULL, ColoreControllo varchar(16) NOT NULL, ColoreLimiteLow INTEGER NOT NULL, ColoreLimiteHigh INTEGER NOT NULL, ScattaFoto BOOLEAN NOT NULL, Exp1 INTEGER NOT NULL DEFAULT (0), Exp2 INTEGER NOT NULL DEFAULT (0), ';
                      tempstr := tempstr + 'Exp3 INTEGER NOT NULL DEFAULT (0), Exp4 INTEGER NOT NULL DEFAULT (0), Exp5 INTEGER NOT NULL DEFAULT (0), Exp6 INTEGER NOT NULL DEFAULT (0), Exp7 INTEGER NOT NULL DEFAULT (0), Exp8 INTEGER NOT NULL DEFAULT (0))';
                      FDConnection1.ExecSQL(tempstr);
                      //Reinseriamo i dati nella nuova tabella
                      //nei nuovi campi
                      tempstr := 'INSERT INTO '+tempDBList[i]+' (DataOraUTC, Prg, SX, SubPasso, EsitoPasso, TipoControllo, ColoreControllo, "ColoreLimite(intensita)", ScattaFoto, Exp1, Exp2, Exp3, Exp4, Exp5, Exp6, Exp7, Exp8) ';
                      //dai vecchi  campi
                      tempstr := tempstr + 'SELECT DataOraUTC, Prg, SX, SubPasso, EsitoPasso, TipoControllo, ColoreControllo, "ColoreLimite(intensita)", ScattaFoto, Exp1, Exp2, Exp3, Exp4, Exp5, Exp6, Exp7, Exp8 FROM temp_'+tempDBList[i];
                      FDConnection1.ExecSQL(tempstr);
                      //Eliminiamo la vecchia tabella
                      FDConnection1.ExecSQL('DROP TABLE temp_'+tempDBList[i]);
                      //Creiamo gli eventuali indici
                      // 'CREATE UNIQUE INDEX IDX_IndiceSubPasso ON VisSubPassi (DataOraUTCs, Prg, SubPasso);
                      FDConnection1.ExecSQL('PRAGMA foreign_keys = 0');
                    end
                  else
                    begin
                      //Creiamo la nuova tabella
                      tempstr := 'CREATE TABLE if not exists '+tempDBList[i]+' (DataOraUTC DATETIME NOT NULL, Prg INTEGER NOT NULL, SX BOOLEAN NOT NULL, SubPasso INTEGER NOT NULL, EsitoPasso BOOLEAN NOT NULL, ';
                      tempstr := tempstr + 'TipoControllo varchar(16) NOT NULL, ColoreControllo varchar(16) NOT NULL, ColoreLimiteLow INTEGER NOT NULL, ColoreLimiteHigh INTEGER NOT NULL, ScattaFoto BOOLEAN NOT NULL, Exp1 INTEGER NOT NULL DEFAULT (0), Exp2 INTEGER NOT NULL DEFAULT (0), ';
                      tempstr := tempstr + 'Exp3 INTEGER NOT NULL DEFAULT (0), Exp4 INTEGER NOT NULL DEFAULT (0), Exp5 INTEGER NOT NULL DEFAULT (0), Exp6 INTEGER NOT NULL DEFAULT (0), Exp7 INTEGER NOT NULL DEFAULT (0), Exp8 INTEGER NOT NULL DEFAULT (0))';
                      FDConnection1.ExecSQL(tempstr);
                    end;
                end;
              VisVerificaColore:
                begin
                  //Per ogni tabella elencata, cerchiamose esiste già nel database
                  if tempelenco.IndexOf(tempDBList[i]) >=0 then
                    begin
                      //Trasferiamo i dati della tabella esistente e creiamo quella nuova, ritrasferendo tutti i dati.
                      FDConnection1.ExecSQL('PRAGMA foreign_keys = 0');
                      FDConnection1.ExecSQL('CREATE TABLE temp_'+tempDBList[i]+' AS SELECT * FROM '+tempDBList[i]);
                      FDConnection1.ExecSQL('DROP TABLE '+tempDBList[i]);
                      //Creiamo la nuova tabella
                      //tempstr := 'CREATE TABLE if not exists '+tempDBList[i]+' (DataOraUTC DATETIME NOT NULL REFERENCES VisSubPassi (DataOraUTC), Prg INTEGER NOT NULL REFERENCES VisSubPassi (Prg), SX BOOLEAN NOT NULL REFERENCES VisSubPassi (SX), ';
                      //tempstr := tempstr + 'SubPasso INTEGER NOT NULL REFERENCES VisSubPassi (SubPasso), NumRoi INTEGER NOT NULL, EsitoRoi BOOLEAN NOT NULL, TipoRoi VARCHAR (16) NOT NULL, ';
                      tempstr := 'CREATE TABLE if not exists '+tempDBList[i]+' (DataOraUTC DATETIME NOT NULL, Prg INTEGER NOT NULL, SX BOOLEAN NOT NULL, ';
                      tempstr := tempstr + 'SubPasso INTEGER NOT NULL, NumRoi INTEGER NOT NULL, EsitoRoi BOOLEAN NOT NULL, TipoRoi VARCHAR (16) NOT NULL, ';
                      tempstr := tempstr + 'MinNumPixel INTEGER NOT NULL, MaxNumPixel INTEGER NOT NULL, ConteggioPixel INTEGER NOT NULL)';
                      FDConnection1.ExecSQL(tempstr);
                      //Reinseriamo i dati nella nuova tabella
                      //nei nuovi campi
                      tempstr := 'INSERT INTO '+tempDBList[i]+' (DataOraUTC, Prg, SX, SubPasso, NumRoi, EsitoRoi, TipoRoi, MinNumPixel, MaxNumPixel, ConteggioPixel) ';
                      //dai vecchi  campi
                      tempstr := tempstr + 'SELECT DataOraUTC, Prg, SX, SubPasso, NumRoi, EsitoRoi, TipoRoi, MinNumPixel, MaxNumPixel, ConteggioPixel FROM temp_'+tempDBList[i];
                      FDConnection1.ExecSQL(tempstr);
                      //Eliminiamo la vecchia tabella
                      FDConnection1.ExecSQL('DROP TABLE temp_'+tempDBList[i]);
                      //Creiamo gli eventuali indici
                      // 'CREATE UNIQUE INDEX IDX_IndiceSubPasso ON VisSubPassi (DataOraUTCs, Prg, SubPasso);
                      FDConnection1.ExecSQL('PRAGMA foreign_keys = 0');
                    end
                  else
                    begin
                      //Creiamo la nuova tabella
                      tempstr := 'CREATE TABLE if not exists '+tempDBList[i]+' (DataOraUTC DATETIME NOT NULL, Prg INTEGER NOT NULL, SX BOOLEAN NOT NULL, ';
                      tempstr := tempstr + 'SubPasso INTEGER NOT NULL, NumRoi INTEGER NOT NULL, EsitoRoi BOOLEAN NOT NULL, TipoRoi VARCHAR (16) NOT NULL, ';
                      tempstr := tempstr + 'MinNumPixel INTEGER NOT NULL, MaxNumPixel INTEGER NOT NULL, ConteggioPixel INTEGER NOT NULL)';
                      FDConnection1.ExecSQL(tempstr);
                    end;
                end;

//OMISSIS ............QUI SI CONTINUA CON LE ALTRE TABELLE

            end;
        end;
        //Andiamo ad aggiornare il campo versione se siamo arrivati sino a qui
        FDQuery1.Active := false;
        FDQuery1.SQL.Clear;
        FDQuery1.SQL.Add('select Visione from Versione');
        FDQuery1.Active := true;
        FDQuery1.First;
        FDQuery1.Edit;
        FDQuery1.FieldByName('Visione').Value := VersioneDB;
        FDQuery1.Post;
      end;
    fUsaDBforTrace := true;   //Se tutto OK allora settiamo il flag per l'uso del database.
    except on e:exception do
      begin
        if not trystrtoint(e.ClassName, tempresult) then
          begin
            tempresult := BaseError_SW+$FFFF;
          end;
        Result := tempResult;
        sErrore := '$'+inttohex(tempresult,8)+': '+e.Message;
      end;
  end;
  tempelenco.Free;
end.;