Lazarus

Programming => Databases => Topic started by: 1HuntnMan on May 17, 2025, 03:39:14 pm

Title: [SOLVED] Database Locked Exception
Post by: 1HuntnMan on May 17, 2025, 03:39:14 pm
Anyone know why this occurs. This is an application I converted from TDbf to SQLite3. Testing, I can add 1 record or 4 or 5, doesn't matter, if I close the form I get an EDatabaseError with message: database is locked.

If I relaunch the app all newly added records are lost. To save a record, I'm just using the DBNav->Post. Closing the application I'm just Closing the main form.
Code: Pascal  [Select][+][-]
  1. procedure TFrmCntksMain.MnuFileExitClick(Sender: TObject);
  2. begin
  3.   Close;
  4. end;
  5.  
Also when the mainform is closed I was Form.Free but commented that out but makes no difference. It's as if adding records, posting, they aren't really being committed to the SQLite3 database.
Title: Re: Database Locked Exception
Post by: 1HuntnMan on May 17, 2025, 04:29:24 pm
I figured it out from ChatGPT. I haven't seen this in any of the documentation on line such as SQLdb Tutorial's 0-4, but this is what you do to correctly close an application when you have active connections, queries, transactions:
Code: Pascal  [Select][+][-]
  1. procedure TFrmCntksMain.SpdBtnCloseClick(Sender: TObject);
  2. begin
  3.   if QryCntks.Active then
  4.     QryCntks.Close;
  5.   if TransCntks.Active then
  6.     begin
  7.       try
  8.         TransCntks.Commit;
  9.       except
  10.         on E: Exception do
  11.         begin
  12.           //-> Handle if rollback is required...
  13.           TransCntks.Rollback;
  14.         end;
  15.       end;
  16.     end;
  17.   if ConnectCntks.Connected then
  18.     Close;
  19. end;
  20.  

But, not finished, need to close all connections, queries and transactions before closing the application. I have 3 other tables connected.
Title: Re: Database Locked Exception
Post by: paweld on May 17, 2025, 04:52:48 pm
In order for the changes to save to the database you must commit the transaction before closing the connection.
You can do this by calling:
Code: Pascal  [Select][+][-]
  1. SQLTransaction1.Commit;
before closing the connection.
But you can also set auto-commit transactions in the TSQLQuery options:
Code: Pascal  [Select][+][-]
  1. SQLQuery1.Options := [sqoAutoApplyUpdates, sqoAutoCommit];  
Title: Re: Database Locked Exception
Post by: 1HuntnMan on May 17, 2025, 11:21:48 pm
PAWeld, thanks! I have the commit's in my code and that's working now but getting an exception everytime close the app after testing adding records, but at least it's saving all my testing/transactions.  See the attached ScrnShot. I'm going to run the app and step thru right when I close the app to see where it's burbing with the Access Violation.
Title: Re: Database Locked Exception
Post by: cdbc on May 18, 2025, 12:58:51 am
Hi
Do you have more <Connection>s to the database?!?
If so, that could be what's locking the database...
The correct way is to have ONE connection and then share that among components...
Regards Benny
Title: Re: Database Locked Exception
Post by: 1HuntnMan on May 20, 2025, 10:23:50 pm
cdbc, et. al.
Here's the way I'm closing the app.
Code: Pascal  [Select][+][-]
  1. procedure TFrmCntksMain.SpdBtnCloseClick(Sender: TObject);
  2. begin
  3.   //-> Close the CONTACTS table
  4.   if QryCntks.Active then
  5.     QryCntks.Close;
  6.     if TransCntks.Active then
  7.       begin
  8.         try
  9.           TransCntks.Commit;
  10.         except on E: Exception do
  11.           begin
  12.             //-> Handle if rollback is required...
  13.             TransCntks.Rollback;
  14.             TransCntks.Active:= False;
  15.           end;
  16.       end;
  17.     end;
  18.     if ConnectCntks.Connected then
  19.       Close;
  20.   //-> Close the STPROVCODES table
  21.   if QryStates.Active then
  22.     QryStates.Close;
  23.   if TransStates.Active then
  24.     begin
  25.       try
  26.         TransStates.Commit;
  27.       except on E: Exception do
  28.         begin
  29.           //-> Handle if rollback is required...
  30.           TransStates.Rollback;
  31.           TransStates.Active:= False;
  32.         end;
  33.       end;
  34.     end;
  35.   if ConnectStates.Connected then
  36.     Close;
  37.   //-> Close CATEGORIES table
  38.   if QryCat.Active then
  39.     QryCat.Close;
  40.   if TransCat.Active then
  41.     begin
  42.       try
  43.         TransCat.Commit;
  44.       except on E: Exception do
  45.         begin
  46.           // Handle if rollback is required...
  47.           TransCat.Rollback;
  48.           TransCat.Active:= False;
  49.         end;
  50.       end;
  51.     end;
  52.   if ConnectCat.Connected then
  53.     Close;
  54.   //-> Close COUNTRIES table
  55.   if QryCntries.Active then
  56.     QryCntries.Close;
  57.   if TransCntries.Active then
  58.     begin
  59.       try
  60.         TransCntries.Commit;
  61.       except on E: Exception do
  62.         begin
  63.           //-> Handle if rollback is required...
  64.           TransCntries.Rollback;
  65.           TransCntries.Active:= False;
  66.         end;
  67.       end;
  68.     end;
  69.   if ConnectCntries.Connected then
  70.     Close;
  71.   FrmCntksMain.Close;
  72. end;
  73.  

When the FrmCntksMain.FormClose is executed, I'm FrmCntksMain.Free the popping up a Quote of the Day in a randomingly generated MessageDlg.

I don't get the Database Locked Exception until just after the Quote of the Day MessageDlg is closed. But at that time, every table should be closed. Should I also close the Database file itself, I haven't read any examples showing closing the database file itself.

Also, the app allows to update the countries, categories and states/provinces tables which can be edit from the main form with File-Open-Categories, Countries, States. Those 3 forms just have a DataSource with the DataSet property pointing back to the main form.Qry. The mainform uses those 3 procedure. I can go to any of those 3 forms and update and save records but when I exit the main form, any updates aren't saving in those 3 tables nor are any updates saving from the main contacts table.

Do you see anything wrong with the way I'm exiting?
Title: Re: Database Locked Exception
Post by: CharlyTango on May 21, 2025, 09:34:17 am
But you can also set auto-commit transactions in the TSQLQuery options:
Code: Pascal  [Select][+][-]
  1. SQLQuery1.Options := [sqoAutoApplyUpdates, sqoAutoCommit];  

IMO there is no need to have seperate transaction Objects for each Query. A single Transaction Object connected to the connection object will suffice. So you get rid of any lines dealing with transactions except a last one that closes the transaction object before closing the connection object.

Where do you store your connection Object including the connections transaction?
Mainform or Data Module?

Do you know which strings are sendt and received to and from your database?
Have you implemented a log file feature where you can see this?

some kind of code example by aircode:

Code: Pascal  [Select][+][-]
  1.    
  2. //put this in your datamodules initializing procedure
  3.  
  4. SQLConn.OnLog:=@DoSQLLog;    
  5. SQLConn.LogEvents:=LogAllEventsExtra;  //either LogAllEvents or LogAllEventsExtra
  6.  
  7.  
  8.  
  9. procedure TctSQLconnect.DoSQLLog(Sender: TSQLConnection; EventType: TDBEventType;
  10.   const Msg: String);
  11. var
  12.   f:TextFile;
  13.   sFileName:string;
  14.   LogString:string;
  15. begin
  16.  
  17.   if not FbLogSQL then exit;
  18.  
  19.   case EventType of
  20.     detCustom: LogString:='Custom';
  21.     detPrepare: LogString:='Prepare';
  22.     detExecute: LogString:='Execute';
  23.     detFetch: LogString:='Fetch';
  24.     detCommit: LogString:='Commit';
  25.     detRollBack: LogString:='RollBack';
  26.     detParamValue: LogString:='ParamValue';
  27.     detActualSQL: LogString:='ActualSQL';
  28.   else ;
  29.     LogString:='Unknown';
  30.   end;
  31.  
  32.   Logstring:=Logstring+' '+ Msg;
  33.  
  34.   WriteToSQLLog(Logstring); //write log to file immediately
  35.  
  36. end;

maybe you get a trace where the database locks.
Usually a SQLite database is locked when another open access to the SQLite database occupies the access. That can be internal (when another/second SQL connection object tries to manipulate data while the primary connection object is not yet closed) or external (if some kind of "DB Browser for SQLite" or "Heidi" blocks the database)

BTW: a common closing of SQLQueries in one procedure of the main form is obsolete for me.
In my mind every form takes care of opening and closing all components it needs. Including database components as TSQLQuery and TDatasource.
In the Event OnFormClose or even better OnCloseQuery you can do all this and tidy up your form. This way all work is done with am mere close of the each form.
Even when closing the application with several open forms this strategy closes all open database components and even the connection securely.





Title: Re: Database Locked Exception
Post by: silvercoder70 on May 21, 2025, 02:04:00 pm
In your code I notice the following ...

  if ConnectStates.Connected then
    Close;

shouldn't that read ...

  if ConnectStates.Connected then
    ConnectStates.Close;

there are a couple of places where I notice that.  :(
Title: Re: Database Locked Exception
Post by: paweld on May 21, 2025, 02:57:36 pm
And above all, for one database (one file) use only one connection (as @cdbc mentioned). And in the attached code you can see that you are using a separate connection for each table - this cannot be the case because the connections will block each other.
Title: Re: Database Locked Exception
Post by: 1HuntnMan on May 21, 2025, 08:05:55 pm
Wow, great! Thought I was on the right track, thanks all for the knowledge, back to the drawing board. Think I'm going to move the one Connect, one Transaction, Queries to a DataModule just so the main form of this test app for learning SQLite isn't cluttered with the Connects, Trans, Qrys and DSrcs for every table.

I'll be back maybe later, probably didn't word this properly, need to study this a bit while I'm redoing eveything, thanks!

This app has one table for managing the Contacts, the other three tables are just for DBLookupCombo's so the user can lookup a state/province, country and a category for the contact. 3 other forms are just for building/maintaining these 3 lookup tables. Thanks...
Title: Re: Database Locked Exception
Post by: cdbc on May 21, 2025, 08:24:46 pm
Hi
To make it clear >> ONE << connection per database!!!
Regards Benny
Title: Re: Database Locked Exception
Post by: 1HuntnMan on May 25, 2025, 06:54:23 pm
Okay, started all over, basically. Dumped all the code that was accessing the tables separately. See below the code snipet that won't compile with this first line:
Code: Pascal  [Select][+][-]
  1. Connect:= TSQLiteConnection.Create(Nil);
  2.  
If I comment out that line, I get a compile but the next line bombs:
Code: Pascal  [Select][+][-]
  1. procedure TFrmCntksMain.FormCreate(Sender: TObject);
  2. //-> Create the connection to the ContactsDB sqlite3 database ...
  3. begin
  4.   //Connect:= TSQLiteConnection.Create(Nil);
  5.   Connect.DatabaseName:= Application.Location + 'ContactsDB.sqlite3';{ This errors when running: 'External
  6.                                  ACCESS VIOLATION' }
  7.   Connect.Connected:= True;
  8.   //-> Create a transaction ...
  9.   Trans.DataBase:= Connect;
  10.   Trans.Action:= caCommit;
  11.   Trans.Active:= True;
  12.   Query.Transaction:= Trans;
  13.   Query.Options:= [sqoAutoApplyUpdates, sqoAutoCommit];
  14.   DSrcCntks.DataSet:= Query;
  15.   Query.SQL.Text:=
  16.    'SELECT * FROM CONTACTS ContactsDB ORDER BY LASTNAME, FIRSTNAME, MI';
  17.   Query.UpdateSQL.Text:=
  18.    'UPDATE CONTACTS SET CNTKID=:CNTKID, DATESTAMP=:DATESTAMP, LASTNAME=:LASTNAME, '+
  19.    'FIRSTNAME=:FIRSTNAME, MI=:MI, CATEGORY=:CATEGORY, DOB=:DOB, COMPANY=:COMPANY, '+
  20.    'TITLE=:TITLE, SPOUSE=:SPOUSE, ADDRESS=:ADDRESS, ADDRESS2=:ADDRESS2, '+
  21.    'CITYTOWN=:CITYTOWN, COUNTY=:COUNTY, STATEPROV=:STATEPROV, POSTALCODE=:POSTALCODE, '+
  22.    'COUNTRY=:COUNTRY, CELLNO=:CELLNO, MAINPHONE=:MAINPHONE, PHONE=:PHONE, '+
  23.    'FAX=:FAX, EMAIL=:EMAIL, EMAIL2=:EMAIL2, WEBURL=:WEBURL, NOTES=:NOTES '+
  24.    'WHERE CNTKID=:OLD_CNTKID';
  25. end;
  26.  
  27. procedure TFrmCntksMain.FormShow(Sender: TObject);
  28. begin
  29.   BitBtnSaveCntk.Enabled:= False;
  30.   BitBtnCancel.Enabled:= False;
  31.   //--->
  32.   Query.Open;
  33. ...
  34.  

Title: Re: Database Locked Exception
Post by: cdbc on May 25, 2025, 07:32:48 pm
Hi
If you would call it its correct type-name (TSQLite3Connection), I think it should compile  %) :P 8-)
Tsk, tsk, tsk...
Regards Benny
Title: Re: Database Locked Exception
Post by: Handoko on May 25, 2025, 08:49:53 pm
If I am allowed, I would give a piece of advice:
Move all database related code into a separate module.

After years of writing code, I learn that code becomes harder to manage if we don't separate them properly. If the program only contains 1 or 2 forms then it is okay to put all the code in the form. But if there are many forms accessing same thing, in this case database, it will lead to spaghetti code.
https://en.wikipedia.org/wiki/Spaghetti_code (https://en.wikipedia.org/wiki/Spaghetti_code)

Most of OP problems, can be minimized if the non-GUI code separated properly from the forms. The code become more readable, less places for bugs to hide. If done properly, you can see clearly there is no reason to have more than 1 connection, as pointed out by cdbc.

Let forms do what they intended to do. Collecting information from users and showing information to users.
Title: Re: Database Locked Exception
Post by: 1HuntnMan on May 25, 2025, 09:55:28 pm
cdbc, thanks, that fixed that error.
Handoko, trying to get there. I have a working app, a Photographer's Mgt. app that I wrote a few years ago but uses TDbf. I has 18 tables, i.e. Dbf files. But, my goal is to convert it to SQLite3 but just learning by designing a simple Contact Mgt. app which only has 4 tables in the database. The main table is Contacts and the other 3 are just lookup tables for States/Provinces, Countries and Categories. Now, I'm getting an error at the Trans.Database:= Connect.  (External ACCESS VIOLATION AT LINE 187 Trans.DataBase:= Connect;). I'll figure that out here in a bit probably... thanks
Title: Re: Database Locked Exception
Post by: 1HuntnMan on May 26, 2025, 06:31:35 pm
Okay thanks all, finally figured out the SQL way of thinking. So, from everyone's advice I have a working app but just for the main table Contacts. Now, I don't need separate Connections or Transactions for each table in a SQLite database. That was my think, kind of like TDBf thinking.
So, for the other 3 tables which are lookup tables for each Contact record, I just need a Query and DataSource for each table in the database, correct? So, just define separate Queries, Query.SQL.Text for the other 3 tables. A LookUpCombo has the DataSource for the Contacts table and the lookup part is the DataSource for the States/Prov table pointing to the States/Prov query.
I also like the advice when closing the mainform, using the OnCloseQuery event to tidy up and close the query(s), commit any transactions and close the connection.
Title: Re: Database Locked Exception
Post by: 1HuntnMan on May 27, 2025, 04:20:56 pm
Okay, this app was compiling and running without the other 3 lookup tables so added the Queries for those and made sure the DataSource's were in their proper places in the LookUpCombo's for States, Countries and Categories. But now I'm getting a new error when I 'Run' but it compiles without error. See the attachment, "Database not Assigned". I tried to step thru but couldn't figure it out. Here's the Create and Show procedures:
Code: Pascal  [Select][+][-]
  1. procedure TFrmCntksMain.FormCreate(Sender: TObject);
  2. //-> Create the connection to the ContactsDB sqlite3 database ...
  3. begin
  4.   ConnectCntks:= TSQLite3Connection.Create(Nil);
  5.   ConnectCntks.DatabaseName:= Application.Location + 'ContactsDB.sqlite3';
  6.   //-> Create a transaction ...
  7.   ConnectCntks.Connected:= True;
  8.   TransCntks:= TSQLTransaction.Create(ConnectCntks);
  9.   //-> Point to the database instance ...
  10.   TransCntks.DataBase:= ConnectCntks;
  11.   TransCntks.Action:= caCommit;
  12.   TransCntks.Active:= True;
  13.   //-> Now open the database ...
  14.   //ConnectCntks.Connected:= True;
  15.   QryCntks.Transaction:= TransCntks;
  16.   QryCntks.Options:= [sqoAutoApplyUpdates, sqoAutoCommit];
  17.   QryCntks.DataBase:= ConnectCntks;
  18.   //QryCntks:= TSQLQuery.Create(ConnectCntks);
  19.   //QryCntks.TransCntks:= True;
  20.   //-> Select CONTACTS Table query...
  21.   DSrcCntks.DataSet:= QryCntks;
  22.   QryCntks.SQL.Text:=
  23.    'SELECT * FROM CONTACTS ContactsDB ORDER BY LASTNAME, FIRSTNAME, MI';
  24.   QryCntks.UpdateSQL.Text:=
  25.    'UPDATE CONTACTS SET CNTKID=:CNTKID, DATESTAMP=:DATESTAMP, LASTNAME=:LASTNAME, '+
  26.    'FIRSTNAME=:FIRSTNAME, MI=:MI, CATEGORY=:CATEGORY, DOB=:DOB, COMPANY=:COMPANY, '+
  27.    'TITLE=:TITLE, SPOUSE=:SPOUSE, ADDRESS=:ADDRESS, ADDRESS2=:ADDRESS2, '+
  28.    'CITYTOWN=:CITYTOWN, COUNTY=:COUNTY, STATEPROV=:STATEPROV, POSTALCODE=:POSTALCODE, '+
  29.    'COUNTRY=:COUNTRY, CELLNO=:CELLNO, MAINPHONE=:MAINPHONE, PHONE=:PHONE, '+
  30.    'FAX=:FAX, EMAIL=:EMAIL, EMAIL2=:EMAIL2, WEBURL=:WEBURL, NOTES=:NOTES '+
  31.    'WHERE CNTKID=:OLD_CNTKID';
  32.   //-> Select STATES Table query...
  33.   DSrcStates.DataSet:= QryStates;
  34.   QryStates.SQL.Text:= 'SELECT * FROM STPROVCODES ContactsDB ORDER BY STPROVCODE';
  35.   QryStates.UpdateSQL.Text:=
  36.    'UPDATE STPROVCODES SET STPROVCODE=:STPROVCODE, STPROVNAME=:STPROVCODE, '+
  37.    'STPROVISO=:STPROVISO WHERE STPROVCODE=:OLD_STPROVCODE';
  38.   //-> Select COUNTRIES Table query...
  39.   DSrcCntries.DataSet:= QryCntries;
  40.   QryCntries.SQL.Text:= 'SELECT * FROM COUNTRIES ContactsDB ORDER BY CODE';
  41.   QryCntries.UpdateSQL.Text:= 'UPDATE COUNTRIES SET CODE=:CODE, COUNTRY=:COUNTRY'+
  42.    'WHERE CODE=:OLD_CODE';
  43.   //-> Select CATEGORIES Table query...
  44.   DSrcCat.DataSet:= QryCat;
  45.   QryCat.SQL.Text:= 'SELECT * FROM CATEGORIES ContactsDB ORDER BY CATID';
  46.   QryCat.UpdateSQL.Text:= 'UPDATE CATEGORIES SET CATID=:CATID, CATEGORY=:CATEGORY'+
  47.    'WHERE CATID=:OLD_CATID';
  48.   //-> Clear/Assign index items to the CmboBxSelIndex component...
  49.   CmboBxSelIndex.Items.Clear;
  50.   CmboBxSelIndex.Items.Add('Name (L,F,M)');
  51.   CmboBxSelIndex.Items.Add('Company');
  52.   CmboBxSelIndex.Items.Add('Category');
  53.   CmboBxSelIndex.Items.Add('City/Town');
  54.   CmboBxSelIndex.Items.Add('State/Province');
  55.   CmboBxSelIndex.Items.Add('Country');
  56. end;
  57.  
  58. procedure TFrmCntksMain.FormShow(Sender: TObject);
  59. begin
  60.   BitBtnSaveCntk.Enabled:= False;
  61.   BitBtnCancel.Enabled:= False;
  62.   //-> Open Contacts tables queries
  63.   QryCntks.Open;
  64.   QryStates.Open;
  65.   QryCntries.Open;
  66.   QryCat.Open;
  67.   EditTTLCntks.ReadOnly:= False;
  68.   IntRecCount:= DSrcCntks.DataSet.RecordCount;
  69.   EditTTLCntks.Text:= IntToStr(IntRecCount); //-> Show total Contacts...
  70.   EditTTLCntks.ReadOnly:= True;
  71. end;
  72.  
Title: Re: Database Locked Exception
Post by: cdbc on May 27, 2025, 04:34:45 pm
Hi
No wonder... I only see this one:
Code: Pascal  [Select][+][-]
  1. QryCntks.DataBase:= ConnectCntks;
Where are the others?!?
Regards Benny
Title: Re: Database Locked Exception
Post by: CharlyTango on May 28, 2025, 05:10:54 pm

Just for your information: qualified help can only be given if you know WHERE the error message occurs.
In other words, at which line of code. This requires that you have debugged the code at least once up to the error line.

Just showing an error message without a reference would get you more than one reprimand in other forums.

Back to the topic: My guess goes in the same direction as @cdbc that the error message occurred when opening.
Code: Pascal  [Select][+][-]
  1. QryStates.Open;
And the error message is meaningful. The TSQLQuery QryStates is missing the assignment of the database connection via which it should fetch the data.
So something like
Code: Pascal  [Select][+][-]
  1. QryStates.DataBase:= ConnectCntks ;;
  2. QryCntries.DataBase:= ConnectCntks;
  3. QryCat.DataBase:= ConnectCntks;

Every TSQLQuery and also every other SQL component needs an associated database connection. In your case, this is ConnectCntks. All SQL components connect to the database via the same database connection, but you also have to assign it.
Title: Re: Database Locked Exception
Post by: 1HuntnMan on May 28, 2025, 07:55:36 pm
Yes, I will do that from now on but figured it out by looking at my code and using debug to figure out that I had forgotten to assign the DSrcStates, DSrcCntries and DSrcCategories to the proper Queries, i.e. DSrcStates.DataSet:= QryStates; and also the other 2. I had QryStates, Cntries and Categories assigned to their corresponding datasource but the DBLookUpCombos weren't active because the DSrc components weren't assigned to the proper Queries.

Next and probably last question for awhile. I've tested the main form, adding records, deleting and editing, etc. All appears to be working properly.  But, I have 3 other forms that are launched from the mainform's menu File - Open - States/Provinces, Countries and Categories. These 3 forms are for just maintaining the States, Countries and Categories tables.  Each form is just a DBNavigator, DbGrid and a Close btn.  The forms are in the Uses of the main form and each form only has a DataSource component -  DataSet assigned to the main forms Query, i.e. FrmCntksMain.QryCat. Same setup on the other 2 forms. I can open and edit, delete and add new records but when I close the form the database is closed on everything, even the main form.

Can anyone advise how to approach this correctly?
 
Title: Re: Database Locked Exception
Post by: CharlyTango on May 28, 2025, 09:01:35 pm
why your application closes the database connection cannot be estimated without knowing the code.
As i mentioned before: My crystal ball is currently being serviced

I have provided you with an example in which you can see all the techniques I have mentioned in my posts, including a way to make your master data maintenance

https://github.com/CharlyTango/SQLite_simple (https://github.com/CharlyTango/SQLite_simple)
Title: Re: Database Locked Exception
Post by: 1HuntnMan on May 31, 2025, 11:10:05 pm
Charley, thanks for the demo. I made some changes and this test app for learning SQLite3 runs just fine except when I save a new record. It saves but then closes the database??? Below is the code for the main part of the Main program as far as saving a new record. It saves but closes the database. I've attempted to step thru but too many forms pop-up to figure out why the database is closing.
Code: Pascal  [Select][+][-]
  1. var
  2.   FrmCntksMain: TFrmCntksMain;
  3.   Keys: TStringList;
  4.   IntRecCount: Integer;
  5.   ConnectCntks : TSQLite3Connection;
  6.   TransCntks   : TSQLTransaction;
  7.   QryCntks     : TSQLQuery;
  8.   QryStates    : TSQLQuery;
  9.   QryCntries   : TSQLQuery;
  10.   QryCat       : TSQLQuery;
  11.  
  12. implementation
  13.  
  14. {$R *.lfm}
  15.  
  16. { TFrmCntksMain }
  17.  
  18. procedure TFrmCntksMain.FormCreate(Sender: TObject);
  19. begin
  20.   SetupDB;
  21.   ConnectCntks.Open; //-> Open the Contacts Database...
  22.   //-> Clear/Assign index items to the CmboBxSelIndex component...
  23.   CmboBxSelIndex.Items.Clear;
  24.   CmboBxSelIndex.Items.Add('Name (L,F,M)');
  25.   CmboBxSelIndex.Items.Add('Company');
  26.   CmboBxSelIndex.Items.Add('Category');
  27.   CmboBxSelIndex.Items.Add('City/Town');
  28.   CmboBxSelIndex.Items.Add('State/Province');
  29.   CmboBxSelIndex.Items.Add('Country');
  30. end;
  31.  
  32. procedure TFrmCntksMain.SetupDB;
  33. var
  34.   CntksDB : string = 'ContactsDB.sqlite3';
  35. begin
  36.   //-> Create the connection to the ContactsDB sqlite3 database ...
  37.   CntksDB:= Application.Location + CntksDB;
  38.   if not FileExists(CntksDB) then begin
  39.     showmessage('Contacts Database file '+CntksDB+', is missing!');
  40.     exit;
  41.   end;
  42.   ConnectCntks:= TSQLite3Connection.Create(Nil);
  43.   ConnectCntks.DatabaseName:= CntksDB;
  44.   //-> Create the transaction for the connection ...
  45.   { Next line Error??? Error: identifier idents no member "TSQLTransaction"
  46.     TransCntks.TSQLTransaction.Create(ConnectCntks); }
  47.   TransCntks.DataBase:= ConnectCntks;
  48.   TransCntks.Action:= caCommit;
  49.   TransCntks.Active:= True;
  50.   //-> Now Query/Open/Connect to the ContactsDB database ...
  51.   ConnectCntks.Connected:= True;
  52.   QryCntks.Transaction:= TransCntks;
  53.   QryCntks.Options:= [sqoAutoApplyUpdates,sqoAutoCommit];
  54.   QryCntks.DataBase:= ConnectCntks;
  55. end;
  56.  
  57. procedure TFrmCntksMain.FormShow(Sender: TObject);
  58. begin
  59.   //-> Create/Select the CONTACTS Table query...
  60.   QryCntks.SQL.Text:=
  61.    'SELECT * FROM CONTACTS ContactsDB ORDER BY LASTNAME, FIRSTNAME, MI';
  62.   QryCntks.UpdateSQL.Text:=
  63.    'UPDATE CONTACTS SET CNTKID=:CNTKID, DATESTAMP=:DATESTAMP, LASTNAME=:LASTNAME, '+
  64.    'FIRSTNAME=:FIRSTNAME, MI=:MI, CATEGORY=:CATEGORY, DOB=:DOB, COMPANY=:COMPANY, '+
  65.    'TITLE=:TITLE, SPOUSE=:SPOUSE, ADDRESS=:ADDRESS, ADDRESS2=:ADDRESS2, '+
  66.    'CITYTOWN=:CITYTOWN, COUNTY=:COUNTY, STATEPROV=:STATEPROV, POSTALCODE=:POSTALCODE, '+
  67.    'COUNTRY=:COUNTRY, CELLNO=:CELLNO, MAINPHONE=:MAINPHONE, PHONE=:PHONE, '+
  68.    'FAX=:FAX, EMAIL=:EMAIL, EMAIL2=:EMAIL2, WEBURL=:WEBURL, NOTES=:NOTES '+
  69.    'WHERE CNTKID=:OLD_CNTKID';
  70.   DSrcCntks.DataSet:= QryCntks;
  71.   //-> Create/Select the STATES Table query...
  72.   QryStates.DataBase:= ConnectCntks;
  73.   QryStates.Options:= [sqoAutoApplyUpdates, sqoAutoCommit];
  74.   QryStates.SQL.Text:= 'SELECT * FROM STPROVCODES ContactsDB ORDER BY STPROVCODE';
  75.   QryStates.UpdateSQL.Text:=
  76.    'UPDATE STPROVCODES SET STPROVCODE=:STPROVCODE, STPROVNAME=:STPROVCODE, '+
  77.    'STPROVISO=:STPROVISO WHERE STPROVCODE=:OLD_STPROVCODE';
  78.   DSrcStates.DataSet:= QryStates;
  79.   //-> Create/Select the COUNTRIES Table query...
  80.   QryCntries.DataBase:= ConnectCntks;
  81.   QryCntries.Options:= [sqoAutoApplyUpdates, sqoAutoCommit];
  82.   QryCntries.SQL.Text:= 'SELECT * FROM COUNTRIES ContactsDB ORDER BY CODE';
  83.   QryCntries.UpdateSQL.Text:= 'UPDATE COUNTRIES SET CODE=:CODE, COUNTRY=:COUNTRY'+
  84.    'WHERE CODE=:OLD_CODE';
  85.   DSrcCntries.DataSet:= QryCntries;
  86.   //-> Create/Select the CATEGORIES Table query...
  87.   QryCat.DataBase:= ConnectCntks;
  88.   QryCat.Options:= [sqoAutoApplyUpdates, sqoAutoCommit];
  89.   QryCat.SQL.Text:= 'SELECT * FROM CATEGORIES ContactsDB ORDER BY CATID';
  90.   QryCat.UpdateSQL.Text:= 'UPDATE CATEGORIES SET CATID=:CATID, CATEGORY=:CATEGORY'+
  91.    'WHERE CATID=:OLD_CATID';
  92.   DSrcCat.DataSet:= QryCat;
  93.   //-> Open all Contacts Database table queries...
  94.   QryCntks.Open;
  95.   QryStates.Open;
  96.   QryCntries.Open;
  97.   QryCat.Open;
  98.   //-> Show total Contacts...
  99.   EditTTLCntks.ReadOnly:= False;
  100.   IntRecCount:= DSrcCntks.DataSet.RecordCount;
  101.   EditTTLCntks.Text:= IntToStr(IntRecCount);
  102.   EditTTLCntks.ReadOnly:= True;
  103.   //-> Disable Save/Cancel buttons...
  104.   BitBtnSaveCntk.Enabled:= False;
  105.   BitBtnCancel.Enabled:= False;
  106. end;
  107.  
  108. procedure TFrmCntksMain.BitBtnSaveCntkClick(Sender: TObject);
  109. begin
  110.   if DSrcCntks.DataSet.Modified then
  111.     DSrcCntks.DataSet.Post;
  112. end;
  113.  
Title: Re: Database Locked Exception
Post by: CharlyTango on June 01, 2025, 12:00:21 am
At first glance, I see SELECT * in the queries, which in many cases leads to problems because then the estimation of the TSQLQuery what the primary key could be does not work.
I have select * in my example because I only display the data in the grid but do not update it.

Code: Pascal  [Select][+][-]
  1. IntRecCount:= DSrcCntks.DataSet.RecordCount;

does not give a correct value until all the data in the SELECT has actually been fetched. This is not always the case because the components and the SQL server (in your case SQLite) do not transfer all data but only those that are currently displayed. If scrolled, the data is reloaded.
This means that a RecordCount only makes sense if all data has been loaded.

IMO, after a
Code: Pascal  [Select][+][-]
  1. DSrcCntks.DataSet.Post;

a
Code: Pascal  [Select][+][-]
  1. DSrcCntks.DataSet.ApplyUpdates;
should follow to send the data safely to the server.

Your programme still looks the same as before, so you have not implemented any of our recommendations. I also don't see any logging of the connection.
I cannot determine from this tiny piece of code why your programme is terminating the database connection.

Without the source of a working test programme including the database, I am out of this topic
Title: Re: Database Locked Exception
Post by: 1HuntnMan on June 01, 2025, 03:42:11 pm
Okay, I'll use your example and implement a logging. I don't use the DBGrid other than just scrolling thru the records. I designed this test app. to edit/view/add records via the form of DBEdits, 3 DBLookUpCombos for States/Provinces, Countries and Categories. My test data is only 20 records. The primary key is just the ContactID: Primary Key, ASC, Autoincrement. The first test I did was a SQLite3 database with just one table, States and tested by importing data from a TDBf table with all the states of US, Mexico, Canada and Germany. It works fine, it's just a DBNavigator and a DBGrid. I don't think this is the issue of SELECTING all the records in the table and the DBGrid. I'm going to totally rewrite this from scratch and add your example to create a log. Then maybe I'll be able to figure it out. Be back later and let everyone know ... Also, I'm going to disable updating in the DBGrid, it's just allows scrolling and viewing anyway, my daughter likes it but it's not used for editing, etc. Her database TDbf has 102 records in the Contacts table, mine has 72. That's not a large table. This test only has 20 test records.
Title: Re: Database Locked Exception
Post by: 1HuntnMan on June 01, 2025, 05:39:42 pm
Added ApplyUpdates to the OnClick SaveBtn. It saves:
Code: Pascal  [Select][+][-]
  1. procedure TFrmCntksMain.BitBtnSaveCntkClick(Sender: TObject);
  2. begin
  3.   if DSrcCntks.DataSet.Modified then
  4.     try
  5.       DSrcCntks.DataSet.Post; <- Saves
  6.     finally
  7.       QryCntks.ApplyUpdates; <- Errors here???
  8.     end;
  9. end;
  10.  

Error Finally: Operation cannot be performed on an inactive dataset.
So, now as suspected, it's posted and saved to the table but the DataSet isn't Active.
Thanks
Title: Re: Database Locked Exception
Post by: CharlyTango on June 01, 2025, 11:25:25 pm
and where the hell can I download this test application?

Nobody can tell you exactly what the problem is just from your description. We can speculate forever, but that can't help anyone and certainly not you
Title: Re: Database Locked Exception
Post by: paweld on June 02, 2025, 07:27:08 am
If you set AutoAppluUpdates to True in the options ( https://forum.lazarus.freepascal.org/index.php/topic,71164.msg555204.html#msg555204 ), then you don't need to add a line:
Code: Pascal  [Select][+][-]
  1. DSrcCntks.DataSet.ApplyUpdates;
Title: Re: Database Locked Exception
Post by: egsuh on June 03, 2025, 11:46:44 am
I haven't read all the posts carefully. But with following question,

Quote
I can open and edit, delete and add new records but when I close the form the database is closed on everything, even the main form.

When you commit or rollback a transaction, all datasets (ie. queries) using that transaction are CLOSED.

If you want to commit or rollback transaction while keeping datasets open, use COMMITRETAINING or ROLLBACKRETAINING instead.

BTW, why don't you simply drop components on the form or datamodule, and fill in their properties, and create codes? I know that advanced programmers prefer to define them themselves, but you are not advanced programmer at least in this area. First make your whole program work in inserting, editing, and deleting any record. You are stuck at this stage. After that, consider custom-creating and fine-tuning SQLite3Connection, TSQLTransaction, etc. Too many things are discussed together, from Locked database, closing one form closing all other datasets, to posting and applying updates. Each one are separate subject.
Title: Re: Database Locked Exception
Post by: 1HuntnMan on June 07, 2025, 03:06:19 pm
Want to thank everyone for your input.  I think I finally figured out the correct way to design a small SQLite database and make it work. I've been testing and so far not errors.  Also, the 3 maintenance forms for States, Countries and Categories were just a DBNav, DBGrid and a Close Btn.  Fixed that and removed the Qry, DSrc, etc. components and in the implementation of the main frm added a Uses of the those 3 units. Then added the main unit to Uses in those three units Uses in their interface. So, no Qry, Trans, DSrc on those 3 small forms. Just pointed their DBNavs, DBGrids back to the mainforms DSrcCAT, DSrcCntry, DSrcStats. Again, thanks, moving on to my other larger app to convert from TDbf -> SQLite3!
Title: Re: [SOLVED] Database Locked Exception
Post by: CharlyTango on June 07, 2025, 05:47:49 pm
apparently you don't want to learn from our recommendations.
It is a good programming skill to separate the data access from the graphical elements.
It also makes sense (as long as there are not too many on a form) to place the TSQLQuery and TDatasource directly on the maintenance form.
The TSQLiteConnection and the TTransaction belong in a separate data module, which is made known to the forms via a uses clause.

no matter
I am finally out
TinyPortal © 2005-2018