[Gelöst]Problem mit SQLite und DB locked error

Für Themen zu Datenbanken und Zugriff auf diese. Auch für Datenbankkomponenten.
Antworten
Dr. Console
Beiträge: 8
Registriert: Mi 11. Mai 2016, 12:39
OS, Lazarus, FPC: Winux (L 0.9.xy FPC 2.2.z)
CPU-Target: 64Bit
Kontaktdaten:

[Gelöst]Problem mit SQLite und DB locked error

Beitrag von Dr. Console »

Moin zusammen,

ich suche nun bereits seit gestern nach einer Lösung für mein Problem.
Und zwar schreibe ich grade eine Lagerverwaltung für Medizinprodukte bei mir auf der Arbeit.
Hierzu nutze ich eine SQLite DB.
SELECT abfragen funktionieren einwandfrei, wenn ich jedoch eine INSERT Anweisung ausführen will kommt ein DB-Locked error.
Hier mein Code um die SQL-Befehle auszuführen, sowie die betreffende INSERT Anweisung:

Code: Alles auswählen

procedure TForm1.sqlrequest(SQL: String; param1, param2, param3: String);
begin
     SQLQuery1.Close;
     SQLQuery1.SQL.Text:=SQL;
     if param1 <> '' then SQLQuery1.ParamByName('param1').AsString:= param1;
     if param2 <> '' then SQLQuery1.ParamByName('param2').AsString:= param2;
     if param3 <> '' then SQLQuery1.ParamByName('param3').AsString:= param3;
     SQLQuery1.Prepare;
     SQLQuery1.Open;
end;
 
procedure TForm1.sqlexec(SQL: String; param1, param2, param3: String);
begin
     SQLQuery1.Close;
     //SQLTransaction1.EndTransaction;
     SQLQuery1.SQL.Text:=SQL;
     if param1 <> '' then SQLQuery1.ParamByName('param1').AsString:= param1;
     if param2 <> '' then SQLQuery1.ParamByName('param2').AsString:= param2;
     if param3 <> '' then SQLQuery1.ParamByName('param3').AsString:= param3;
     SQLQuery1.Prepare;
     SQLQuery1.ExecSQL;
     //SQLQuery1.UpdateSQL;
     //SQLQuery1.Close;
     SQLTransaction1.Commit;
end;
 
 
[...weiterer code...]
if response = mrYes then
     begin
          sqlexec('INSERT INTO Lagerv_Chargen (Chargennummer,MHD,idArtikel) VALUES (:param1,:param2,:param3)', Chargennummer, MHD_String, IntToStr(idArtikel));
          //sqlexec('INSERT INTO "main"."Lagerv_Chargen" ("Chargennummer","MHD","idArtikel") VALUES ("Tets","2015-05-11","2")');
          updateChargen();
     end;


Wie man sieht hab ich schon einiges probiert, jedoch leider erfolglos.
Wenn ich das SQLTransaction.Commit weglasse und dafür SQLQuery1.close aufrufe, wird mir der eintrag zwar im Programm angezeigt, jedoch nicht in die DB übernommen.

es wäre schön, wenn mir jemand auf die Sprünge helfen könnte.

NG
Dr. Console
Zuletzt geändert von Dr. Console am Do 12. Mai 2016, 12:47, insgesamt 1-mal geändert.

mse
Beiträge: 2013
Registriert: Do 16. Okt 2008, 10:22
OS, Lazarus, FPC: Linux,Windows,FreeBSD,(MSEide+MSEgui 4.6,git master FPC 3.0.4,fixes_3_0)
CPU-Target: x86,x64,ARM

Re: Problem mit SQLite und DB locked error

Beitrag von mse »

Sqlite3 benötigt für das commiten eines Schreibvorgangs einen exklusiven Lock auf die Datei. Ein nicht abgeschlossener Lesevorgang hält einen nicht exklusiven Lock, welcher den Schreibvorgang verhindert.
Hast du die Datei in der IDE oder einer anderen Anwendung geöffnet? AFAIK liest FCL TSQLQuery standardmässig nur die ersten zehn Records und lässt danach die Transaktion (und damit den Lock) offen. Ist TSQLQuery.PacketRecords = -1 werden alle Daten geladen. Ich bin nicht sicher, ob bei FCL der Lock danach automatisch freigegeben wird. MSEgui benutzt für Sqlite3 standardmässig implizite Transaktionen, dadurch kann dieselbe Datei in der IDE geöffnet sein und in der Anwendung verändert werden. Neuere FCL Versionen bieten diese Funktion auch.
Eine weitere Erklärung des Verhaltens wäre, dass mehrere Transaktionen innerhalb der Anwendung im Spiel sind.
PS: Ich glaube nicht, dass du Sqldb im Sinne des Erfinders verwendest...

Benutzeravatar
af0815
Lazarusforum e. V.
Beiträge: 6209
Registriert: So 7. Jan 2007, 10:20
OS, Lazarus, FPC: FPC fixes Lazarus fixes per fpcupdeluxe (win,linux,raspi)
CPU-Target: 32Bit (64Bit)
Wohnort: Burgenland
Kontaktdaten:

Re: Problem mit SQLite und DB locked error

Beitrag von af0815 »

Du schliesst die Transaktion mit .Commit ab, ich sehe aber nicht ob du die Transaktion gestartest hast (Oder das ganz einfach irgendeiner Automatik überlässt). Bitte auch die Version von Lazarus/fpc, da sich da in den letzten Versionen einiges geändert hat, auch zB. zum einschalten des autocommits etc.

Andreas
Blöd kann man ruhig sein, nur zu Helfen muss man sich wissen (oder nachsehen in LazInfos/LazSnippets).

Dr. Console
Beiträge: 8
Registriert: Mi 11. Mai 2016, 12:39
OS, Lazarus, FPC: Winux (L 0.9.xy FPC 2.2.z)
CPU-Target: 64Bit
Kontaktdaten:

Re: Problem mit SQLite und DB locked error

Beitrag von Dr. Console »

Hallo ihr beiden,

danke für die schnellen antworten, alle SQL-Anfragen werden über die beiden procedures durchgeführt, im restlichen code werden lediglich die SELECT oder INSERT anweisungen sowie die Parameter übergeben.
Von außen ist die DB zeitweise in einem Firefox addon zur bearbeitung geöffnet, aber daran liegt es leider nicht, das hab ich schon getestet.
Zudem funktionieren die SELECT anweisungen jederzeit einwandfrei.
Nur bei INSERT gibts probleme.
Das öffnen der Transaktion überlasse ich der Query (habs nur so im netz gesehen).
In einem Beispiel was ich gefunden hatte wurde schlicht und ergreifend der Befehl an das Query über Q.SQL.Text übergeben und per Query.SQLExec ausgeführt.
Danach noch ein Transaction.commit und laut beispiel sollte es das gewesen sein.

Ich kenne meich jetzt leider auch nicht so gut mit der Materie aus, dass ich viel tiefer gehen könnte, da meine letzte APK mit DB-Anbinung schon ein paar jährchen zurück liegt, jedoch funktionierte es damals fehlerfrei.

NG
Dr. Console

P.S. @mse in wie fern verwende ich die nicht im sinne des Erfinders? :D
Ist doch dafür da um datensätzte wie z.B. Artikel, Chargen & Bestellungen darin zu speichern, oder sehe ich das falsch?
Zuletzt geändert von Dr. Console am Mi 11. Mai 2016, 15:25, insgesamt 1-mal geändert.

mse
Beiträge: 2013
Registriert: Do 16. Okt 2008, 10:22
OS, Lazarus, FPC: Linux,Windows,FreeBSD,(MSEide+MSEgui 4.6,git master FPC 3.0.4,fixes_3_0)
CPU-Target: x86,x64,ARM

Re: Problem mit SQLite und DB locked error

Beitrag von mse »

Dr. Console hat geschrieben:Von außen ist die DB zeitweise in einem Firefox addon zur bearbeitung geöffnet, aber daran liegt es leider nicht, das hab ich schon getestet.

Wie steht's mit der IDE/Lazarus?
Zudem funktionieren die SELECT anweisungen jederzeit einwandfrei.

Sicher, die benötigen auch keinen exklusiven Lock, ein nicht exklusiver reicht.

Dr. Console
Beiträge: 8
Registriert: Mi 11. Mai 2016, 12:39
OS, Lazarus, FPC: Winux (L 0.9.xy FPC 2.2.z)
CPU-Target: 64Bit
Kontaktdaten:

Re: Problem mit SQLite und DB locked error

Beitrag von Dr. Console »

Ich wüsste nicht in wie weit Lazarus / IDE abgesehen von dem was ich in der APK mache daruf zugreifen sollte.
Falls du auf eventuelle doppel zugriffe hinaus möchtest, ich wäre mir solcher nicht bewusst, da ich vor jedem neuen Befehl immer Query.Close aufrufe, was soweit mir bekannt die Connection freigeben sollte.
Falls dem nicht so ist korrigiere mich bitte, wie gesagt hab schon lange nicht mehr mit DBs gearbeitet und musste mir das uch alles im netzt erst wieder zusammen suchen.

NG
Dr. Console

Dr. Console
Beiträge: 8
Registriert: Mi 11. Mai 2016, 12:39
OS, Lazarus, FPC: Winux (L 0.9.xy FPC 2.2.z)
CPU-Target: 64Bit
Kontaktdaten:

Re: Problem mit SQLite und DB locked error

Beitrag von Dr. Console »

Versionen sind folgende:

Lazarus 1.6 (2016-02-14)
FPC 3.0.0
SVN 51630

Als 32bit für Win10

Und hier einmal mein kompletter bisheriger code:

Code: Alles auswählen

var
  Form1: TForm1;
  vollabfrage: String = 'SELECT P.*, E.Name AS E_Name, F.Name AS F_Name, L.Name AS L_Name, A.Name AS A_Name, C.Chargennummer, C.MHD FROM Lagerv_Artikelposten P INNER JOIN Lagerv_Etagen E ON (E.idEtagen = P.idEtagen), Lagerv_Fächer F ON (F.idFächer = P.idFächer), Lagerv_Lagerorte L ON (L.idLagerorte = P.idLagerorte), Lagerv_Artikel A ON (A.idArtikel = P.idArtikel), Lagerv_Chargen C ON (C.idChargen = P.idChargen) ORDER BY P.idLagerorte, P.idEtagen, P.idFächer ASC';
  Artikelposten: array of array[0..1] of Integer;
 
implementation
uses login;
procedure TForm1.MenuItem1Click(Sender: TObject);
begin
     userid:= 0;
     displayname:= '';
     Form2.Show;
     Close;
end;
 
procedure TForm1.Button3Click(Sender: TObject);
var
  Chargennummer: String;
  MHD_String: String;
  MHD: TDate;
  Artikel: String;
  idArtikel: Integer;
  response: Integer;
  FS: TFormatSettings;
begin
     FS.ShortDateFormat := 'YYYY-MM-DD';
     Chargennummer:= LabeledEdit2.Text;
     MHD:= CalendarDialog1.Date;
     MHD_String:= DateToStr(MHD);
     idArtikel:= ComboBox1.ItemIndex+1;
     Artikel:=ComboBox1.Caption;
     response:= MessageDlg('Chargennummer hinzufügen', 'Sind Sie sicher das Sie die Charge "' + Chargennummer + '" mit dem Halbarkeitsdatum "' + MHD_String + '" für den Artikel "'+ Artikel + '" hinzufügen möchten?',mtConfirmation, mbYesNo, 0);
     if response = mrYes then
     begin
          sqlexec('INSERT INTO Lagerv_Chargen (Chargennummer,MHD,idArtikel) VALUES (:param1,:param2,:param3)', Chargennummer, MHD_String, IntToStr(idArtikel));
          //sqlexec('INSERT INTO "main"."Lagerv_Chargen" ("Chargennummer","MHD","idArtikel") VALUES ("Tets","2015-05-11","2")');
          updateChargen();
     end;
end;
 
procedure TForm1.CalendarDialog1Close(Sender: TObject);
begin
     LabeledEdit3.Text:= DateToStr(CalendarDialog1.Date);
end;
 
procedure TForm1.LabeledEdit3Click(Sender: TObject);
begin
  CalendarDialog1.Execute;
end;
 
procedure TForm1.sqlrequest(SQL: String; param1, param2, param3: String);
begin
     SQLQuery1.Close;
     SQLQuery1.SQL.Text:=SQL;
     if param1 <> '' then SQLQuery1.ParamByName('param1').AsString:= param1;
     if param2 <> '' then SQLQuery1.ParamByName('param2').AsString:= param2;
     if param3 <> '' then SQLQuery1.ParamByName('param3').AsString:= param3;
     SQLQuery1.Prepare;
     SQLQuery1.Open;
end;
 
procedure TForm1.sqlexec(SQL: String; param1, param2, param3: String);
begin
     SQLQuery1.Close;
     //SQLTransaction1.EndTransaction;
     SQLQuery1.SQL.Text:=SQL;
     if param1 <> '' then SQLQuery1.ParamByName('param1').AsString:= param1;
     if param2 <> '' then SQLQuery1.ParamByName('param2').AsString:= param2;
     if param3 <> '' then SQLQuery1.ParamByName('param3').AsString:= param3;
     SQLQuery1.Prepare;
     SQLQuery1.ExecSQL;
     //SQLQuery1.UpdateSQL;
     SQLQuery1.Close;
     //SQLTransaction1.Commit;
end;
 
procedure TForm1.updateChargen();
begin
     sqlrequest('SELECT * FROM Lagerv_User WHERE idUser = :param1',IntToStr(userid),'','');
     if SQLQuery1.FieldByName('Admin').AsBoolean then Button4.Enabled:= true;
     sqlrequest('SELECT C.*, A.Name FROM Lagerv_Chargen C INNER JOIN Lagerv_Artikel A ON (A.idArtikel = C.idArtikel)','','','');
     ListView1.Clear;
     while not SQLQuery1.EOF do
     begin
          with ListView1.Items.Add do begin
               Caption:=SQLQuery1.FieldByName('idChargen').AsString;
               SubItems.Add(SQLQuery1.FieldByName('Chargennummer').AsString);
               SubItems.Add(SQLQuery1.FieldByName('MHD').AsString);
               SubItems.Add(SQLQuery1.FieldByName('Name').AsString);
          end;
          SQLQuery1.Next;
     end;
     sqlrequest('SELECT * FROM Lagerv_Artikel A ORDER BY A.idArtikel ASC', '', '', '');
     ComboBox1.Clear;
     while not SQLQuery1.EOF do
     begin
          ComboBox1.Items.Add(SQLQuery1.FieldByName('Name').AsString);
          SQLQuery1.Next;
     end;
end;
 
procedure TForm1.TabSheet3Show(Sender: TObject);
begin
     updateChargen();
end;
 
procedure TForm1.TreeUpdate();
var
   Wurzelknoten, Lagerort, Etage, Fach, Artikel: TTreeNode;
   L_Name, E_name, F_Name, A_Name: String;
   L_Name_changed, E_name_changed, F_Name_changed: Boolean;
   i: Integer =0;
Label
     Neueintragung;
begin
     L_Name_changed:= false;
     E_name_changed:= false;
     F_Name_changed:= false;
     TreeView1.Items.Clear;
     Wurzelknoten := TreeView1.Items.Add(nil, 'Lager Wache');
     sqlrequest(vollabfrage, '', '', '');
     //L_Name := SQLQuery1.FieldByName('L_Name').AsString;
     while not SQLQuery1.EOF do
     begin
          if SQLQuery1.FieldByName('L_Name').AsString = L_Name then
          begin
               goto Neueintragung;
          end
          else
          begin
               L_Name:= SQLQuery1.FieldByName('L_Name').AsString;
               L_Name_changed := true;
          end;
 
          if SQLQuery1.FieldByName('E_Name').AsString = E_Name then
          begin
               goto Neueintragung;
          end
          else
          begin
               E_Name:= SQLQuery1.FieldByName('E_Name').AsString;
               E_Name_changed := true;
          end;
 
          if SQLQuery1.FieldByName('F_Name').AsString = F_Name then
          begin
               goto Neueintragung;
          end
          else
          begin
               F_Name:= SQLQuery1.FieldByName('F_Name').AsString;
               F_Name_changed := true;
          end;
 
          if L_Name_changed then
          begin
               Lagerort := TreeView1.Items.AddChild(Wurzelknoten, SQLQuery1.FieldByName('L_Name').AsString);
          end;
 
          if E_Name_changed then
          begin
               Etage := TreeView1.Items.AddChild(Lagerort, SQLQuery1.FieldByName('E_Name').AsString);
          end;
 
          if F_Name_changed then
          begin
               Fach := TreeView1.Items.AddChild(Etage, SQLQuery1.FieldByName('F_Name').AsString);
          end;
          Neueintragung:
          Artikel:= TreeView1.Items.AddChild(Fach, SQLQuery1.FieldByName('Menge').AsString + 'x ' + SQLQuery1.FieldByName('A_Name').AsString + ' - MHD: ' + SQLQuery1.FieldByName('MHD').AsString);
          Artikelposten[i, 1] := SQLQuery1.FieldByName('idArtikelposten').AsInteger;
          Artikelposten[i, 0] := Artikel.AbsoluteIndex;
          SQLQuery1.Next;
          L_Name_changed:= false;
          E_name_changed:= false;
          F_Name_changed:= false;
          i := i+1;
     end;
end;
 
procedure TForm1.Button2Click(Sender: TObject);
begin
     TreeUpdate();
end;
 
procedure TForm1.FormShow(Sender: TObject);
begin
  Label1.Caption:=IntToStr(login.userid);
  Label2.Caption:=login.displayname;
  sqlrequest(vollabfrage, '', '', '');
  SetLength(Artikelposten, SQLQuery1.RecordCount);
end;
 
procedure TForm1.TabSheet2Show(Sender: TObject);
begin
     TreeUpdate();
end;
 
end.

mse
Beiträge: 2013
Registriert: Do 16. Okt 2008, 10:22
OS, Lazarus, FPC: Linux,Windows,FreeBSD,(MSEide+MSEgui 4.6,git master FPC 3.0.4,fixes_3_0)
CPU-Target: x86,x64,ARM

Re: Problem mit SQLite und DB locked error

Beitrag von mse »

Dr. Console hat geschrieben:Ich wüsste nicht in wie weit Lazarus / IDE abgesehen von dem was ich in der APK mache daruf zugreifen sollte.

Indem du die DB Komponenten im Formular platzierst und TSQLQuery.Active auf true setzt.

Dr. Console
Beiträge: 8
Registriert: Mi 11. Mai 2016, 12:39
OS, Lazarus, FPC: Winux (L 0.9.xy FPC 2.2.z)
CPU-Target: 64Bit
Kontaktdaten:

Re: Problem mit SQLite und DB locked error

Beitrag von Dr. Console »

Also im Objectinspector ist es auf false und wie man oben sieht setzte ich es auch nicht auf true...

Benutzeravatar
af0815
Lazarusforum e. V.
Beiträge: 6209
Registriert: So 7. Jan 2007, 10:20
OS, Lazarus, FPC: FPC fixes Lazarus fixes per fpcupdeluxe (win,linux,raspi)
CPU-Target: 32Bit (64Bit)
Wohnort: Burgenland
Kontaktdaten:

Re: Problem mit SQLite und DB locked error

Beitrag von af0815 »

Es ist die Option der Connection in letzte Zeit neu dazugekommen, ändern BEVOR die Connection geöffnet wird

Code: Alles auswählen

  Connection1.Options:= [scoApplyUpdatesChecksRowsAffected];


Ich mache dann normalerweise sowas

Code: Alles auswählen

  SQL := 'irgend ein statement';
  Con1.Transaction.StartTransaction;
  try
    Con1.ExecuteDirect(SQL);
    Con1.Transaction.Commit;
  except
    on E: Exception do
    begin
      Con1.Transaction.Rollback;
      DebugLn('Error: Cannot blablabla');
      DebugLn('Reason: ' + E.Message);
    end;
  end;
 


Ich mache es hier halt direkt auf der Connection, es geht aber genauso mit einer Query. Die Transaction wird hier indirekt verwendet und muss auf der Form vorhanden sein.

Andreas

BTW: IMHO kannst du dir das Prepare sparen, das hat am Server nur dann Sinn, wenn das Statement gleich bleibt und sich nur die Parameter ändern. Dann hat der SQL Server die Chance das Statement vorzukompileren und zu optimieren (=prepare). Wenn das Statement geändert wird, ist es hinfällig.
Blöd kann man ruhig sein, nur zu Helfen muss man sich wissen (oder nachsehen in LazInfos/LazSnippets).

mse
Beiträge: 2013
Registriert: Do 16. Okt 2008, 10:22
OS, Lazarus, FPC: Linux,Windows,FreeBSD,(MSEide+MSEgui 4.6,git master FPC 3.0.4,fixes_3_0)
CPU-Target: x86,x64,ARM

Re: Problem mit SQLite und DB locked error

Beitrag von mse »

Dr. Console hat geschrieben:Also im Objectinspector ist es auf false und wie man oben sieht setzte ich es auch nicht auf true...

Eine weitere mögliche Ursache für den Lock ist eine TSQLTransaction mit Active = true zur Entwurfszeit. Am einfachsten setzt man Connected aller TSQLit3Connection im Objectinspector auf false; dies setzt auch Active aller angeschlossenen Komponenten zurück. Falls sichergestellt ist, dass nur die Anwendung auf die Datenbank zugreift, muss untersucht werden, warum in der Anwendung gleichzeitig mehrere Transaktionen laufen. Laut der Antwort von af0815 hängt es mit den Optionen der Connection-Komponente zusammen.

Dr. Console
Beiträge: 8
Registriert: Mi 11. Mai 2016, 12:39
OS, Lazarus, FPC: Winux (L 0.9.xy FPC 2.2.z)
CPU-Target: 64Bit
Kontaktdaten:

Re: Problem mit SQLite und DB locked error

Beitrag von Dr. Console »

Sooo... ich habe das problem gefunden -.-

Während meines nächtlichen Programmierens habe ich tatsächlich eine zweite Instanz des DB-Zugriffs vergessen und später übersehen.
Diese sollte sich um das Login kümmern, was sie sehr gut machte, jedoch die Connection nicht wieder schloss.

Also Leute, erst denken, dann gucken und dann fragen....
Sorry für die mühe und trotzdem danke für die schnelle Hilfe.

NG
Dr. Console

Antworten