sqlite harte nuss
-
Hallo zusammen!
Ich habe folgendes Problem und hofffe mir kann jemand helfen.
Ich habe ein Programm entwickelt das den MFC wrapper für SQLite verwendet.
Ich habe 2 funktionen die durch buttons ausgelöst werden.Eine Funktion wird im Normalfall zuerst aufgerufen und enthält nur select anweisungen. Diese funktioniert.
Die zweite funktion verwendet Updateanweisungen und da meldet die DB die fehlermeldung: SQLite_Busy
wenn ich funktion 2 zuerst aufrufe funktioniert sie.
Ich weiss das die db gelockt ist durch die selectanweisung in funktion 1.
Sie sollte aber wieder entsperrt werden wenn die funktion beendet ist.In beider funktionen erstelle ich das db objekt als lokale variable und öffne die datei. das heist das nach beenden der 1. Funktionen das db objekt aufgelöst werden sollte und somit die db geschlossen sein sollte.
Warumm ist sie aber bei aufruf aus der 2.Funktion noch immer gesperrt.
Kann mann die SQLiteDB auch manuell entsperren.
Bitte um Antwort
-
Dir wird kein Mensch helfen können wenn Du nicht sagst worum es eigentlich geht.
Welche Befehle zB ???
-
// Einträge in der DB suchen void CDBtest3Dlg::OnBnClickedButton1() { // TODO: Add your control notification handler code here sqlite=new CDbSQLite; BOOL fTest = sqlite->Open(m_szDbFile);// Öffnen der Datei if (!fTest)// Überprüfen ob öffnen erfolgreich ( Fehlerbehandlung) { CString szError = _T("Could not open "); szError += m_szDbFile; AfxMessageBox(szError); } COLORREF colorRED=RGB(255,0,0); // Rote farbe um einträge zu färben COLORREF colorBLUE=RGB(0,0,255); // blaue Farbe um einträge zu färben // LISTE Löschen m_listCtrl.DeleteAllItems(); // Mit null init. while(m_listCtrl.DeleteColumn(0));// ? nFields=16; // 16 Elemente werden angezeigt CRect rect; // übernimmt die Masse des List Gui m_listCtrl.GetWindowRect(&rect); // die grösse der List in GUI ermitteln nWidth = rect.Width() * 1 / (nFields+1); // errechnen der spaltenbreite der listcontrol. nFields+1 wegen der 1.leeren spalte für min max bezeichnung nCol = 0; // Spalte nRow = 0; // Reihe m_listCtrl.SetGridLines(true); // Gitternetz erzeugen CString szText; // übernimmt query und fehlermeldung // Spaltennamen in listcontrol erstellen m_listCtrl.InsertColumn(1, "*", LVCFMT_CENTER, nWidth, 1); // eine leere spalte am anfang wegen min max bezeichnung for(nCol=0; nCol < nFields; nCol++) { szText = m_szElementeArray.GetAt(nCol); // Spalten(elementname) in die variable szText.MakeUpper(); // Alles in grossbuchstaben konvertieren m_listCtrl.InsertColumn(nCol+2, szText, LVCFMT_CENTER, nWidth, nCol+2); // nCol+2 um die erste leere spalte zu überspringen } // in der liste eine neue spalte(DB: Tabelle) mit kopfname(Tabellenname) erzeugen UpdateData(TRUE);// Benutzereingabe einlesen // Überprüfen ob eine eingabe erfolgt ist if(m_eingabeWerkstoff=="") { szText="!!! ACHTUNG: Bitte Werkstoff eingeben !!!"; m_ausgabe=szText; UpdateData(FALSE); AfxMessageBox(szText); return; } // Abfrage der Werkstofftabelle szText.Format(_T("SELECT * FROM werkstoff WHERE werkstoffnr = '%s'"),m_eingabeWerkstoff); CSqlStatement* stmt=NULL; stmt = sqlite->Statement(szText); // Speichern der abfrage in einer variablen if (stmt != NULL) // Nur wenn stmt einträge beinhaltet { stmt->NextRow(); // Auf datenebene weitergehen CString stmtPrüfen=stmt->ValueString(1); // 1 = werkstoffNr if(stmtPrüfen.Compare(m_eingabeWerkstoff)==0)// Wenn werkstoffnummer vorhanden. 1 wäre fehler { m_ausgabe="! Werkstoff Gefunden !"; m_kontrProbe=stmt->ValueString(0); // 0 = kontrollprobe eintrag m_ausgabePfanne=stmt->ValueString(2); // 2 = pfanne leco eintrag m_ausgabeFA=stmt->ValueString(3); // 3 = FA leco eintrag m_ausgabeProgramm=stmt->ValueString(4); // 4 = Abfunk programm eintrag m_ausgabeNotiz=stmt->ValueString(5); // 5 = Notizeintrag m_listCtrl.InsertItem(1, "MIN"); // eine zeile einfügen für MIN werte m_listCtrl.InsertItem(2, "MAX"); // eine zeile einfügen für MAX werte // Einlesen der werte von elemente CString szMin; CString szMax; for(nCol=0; nCol < nFields;nCol++)// alle elemente einlesen { szText.Format(_T("SELECT * FROM '%s' WHERE werkstoffnr = '%s'"),m_szElementeArray.GetAt(nCol),m_eingabeWerkstoff); stmt = sqlite->Statement(szText); // Speichern der abfrage in einer variablen if(stmt!=NULL) { // Min einlesen -------------------------------------------------------- stmt->NextRow(); //datenebene szMin = stmt->ValueString(1); // 1 = min eintrag if(szMin.Compare("0")!=1){szMin=_T("-");}//wenn kein eintrag dann mit - ersetzten m_listCtrl.SetItem(0, nCol+1, LVIF_TEXT, szMin,0,0,0,0); //0 = erste(min) zeile // Wenn der eintrag einen wert enthält und nicht '-' dann blau färben if(szMin.Compare(_T("-"))==1) { m_listCtrl.SetItemBkColor(0, nCol+1, colorBLUE, TRUE); } // Min einlesen ENDE ----------------------------------------------------- // Max einlesen ---------------------------------------------------------- szMax = stmt->ValueString(2); // 2 = max eintrag if(szMax.Compare("0")!=1){szMax=_T("-");} m_listCtrl.SetItem(1, nCol+1, LVIF_TEXT, szMax,0,0,0,0); // 1 ist 2.(max) zeile // Wenn der eintrag einen wert enthält und nicht '-' dann rot färben if(szMax.Compare(_T("-"))==1) { m_listCtrl.SetItemBkColor(1, nCol+1, colorRED, TRUE); } // Max einlesen ENDE ----------------------------------------------------- } else { AfxMessageBox("konnte Tabelle für Werkstoffe nicht abfragen"); return; } } } else // alle variablen der Ausgabe zurücksetzen { m_kontrProbe=""; m_ausgabePfanne=""; m_ausgabeFA=""; m_ausgabeProgramm=""; m_ausgabeNotiz=""; AfxMessageBox("ACHTUNG ! WERKSTOFF NICHT VORHANDEN!"); m_ausgabe="ACHTUNG ! WERKSTOFF NICHT VORHANDEN!"; } } UpdateData(FALSE); delete(sqlite); sqlite=NULL; } // Einträge in der DB ändern void CDBtest3Dlg::OnBnClickedAendern() { sqlite=new CDbSQLite; bool fTest; // übernimmt ob der DB rückgabe erfolgreich fTest = sqlite->Open(m_szDbFile);// Öffnen der Datei if (!fTest)// Überprüfen ob öffnen erfolgreich ( Fehlerbehandlung) { CString szError = _T("Could not open "); szError += m_szDbFile; AfxMessageBox(szError); } UpdateData(TRUE); CString szText; // übernimmt query und fehlermeldung szText.Format(_T("UPDATE werkstoff SET probe='%s' WHERE werkstoffnr = '%s'"),m_kontrProbe,m_eingabeWerkstoff); fTest = sqlite->DirectStatement(szText); delete(sqlite); sqlite=NULL; }
Wie gesagt: Funktion 2 funktioniert solange ich nicht Funktion 1 aufrufe.
Wenn ich Funktion 1 aufrufe wird die DB gesperrt und leider nicht nach beenden der Funktion freigegeben. Besser gesagt Funktionieren die Funktionen Aber der SQL-Befehl UPDATE gibt die Fehlermeldung: SQLite_Busy zurück.
Das heisst die DatenBank lässt mich nach aufruf der 1.Funktion keine Schreibbefehle mehr aussführen.
Habe es auch versucht ohne die CDbSQLite objekte als Zeiger zu erstellen und funktioniert auch nicht. Habe es mit Zeiger versucht da ich annahm das das Objekt vieleicht nicht richtig aufgelöst wird und somit der Destruktor des Objekts nicht aufgerufen wird. Aber auch nach expliziertem deleten gibt SQLite die Datei nicht frei. Ich muss noch erwähnen das der Destruktor die finalize und DB_Close aufrufe enthält.
In einem mitgelieferten Beispiel des Wrappers funktioniert die vorgehensweise.
-
Ich verwende C, nicht das ++.
Trotzdem: Du öffnest in beiden Funktionen die db, schließt sie aber
anscheinend nicht. Da sie beim Programmstart mit Sicherheit zu sind (werden
beim Programmende geschlossen) könnte es das sein. Damit würde immer der
erste Aufruf laufen.In C sind das die Aufrufe sqlite3_open und sqlite3_close.
-
Doppelpost: Vermeide das bitte in Zukunft.
-
Du hast ein paar "return" Statements in der einen Funktion drinnen, bei denen das "CDbSQLite" Objekt dann nicht gelöscht wird.
Leg das Ding doch statt mit "new" einfach mal lokal an ("CDbSQLite blubb;").
-
Also zum ersten Post das ich die DB schliesen muss:
Es ist nicht nötig die C++ version zu schliesen da dies automatisch vom Destruktor der CDbSQLite Klasse erledigt wird sobald das Objekt zerstörrt wird.
Zum 2. Post:
Auch wenn die Funktion bis zum Ende durchlaufen wird, wird die DB nicht freigegeben. Natürlich habe ich auch schon versucht die DB nur Lokal anzulegen, aber auch dann bleibt die DB nach aufruf der 1. Funktion gesperrt.
Also rauchende Köpfe, bitte helft mir und Danke bis jetzt;
-
Wie es aussieht löscht du auch "stmt" nicht. Vermutlich wird dadurch dass du noch ein offenes "Statement" hast die DB offen gehalten (über welche Mechanismen auch immer, smart pointer oder ref-counting vermutlich, entweder in den Warpper-Klassen oder in SQLite direkt). Nochdazu legst du mehrere "Statements" an ohne diese zu löschen.
Gewöhne dir an Smart-Pointer (std::auto_ptr, boost::shared_ptr etc.) zu verwenden, dann hast du solche Probleme viel seltener (ich kann mich nicht erinnern wann ich das letzte mal ein Memory-Leak bzw. Resource-Leak gesucht hätte).
Und ... die Art von SELECT die du innerhalb der Schleife machst lassen schlechtes Datenbank-Design vermuten. Ich nehme zumindest an dass hier *eine* Detail-Tabelle angebracht wäre anstelle von vielen.
-
Hallo Hustbaer!
DANKE DANKE DU HASTS GELÖST!
Ich habe in der schleife in der Select aufgerufen wird vergessen nach jedem Schleifendurchlauf stmt->reset(); aufzurufen. Da im Beispielprogramm keine schleife verwendet wird haben die diesen aufruf nicht drinnen und somit hat er mir nicht gefehlt. Zu deiner vermutung das ich die stmt nicht delete: Nun es war nicht ausschlaggebend aber natürlich hast du recht:Speicherleck. Muss mir die smartpointer usw mal genauer anschauen.
Zu deiner vermutung über schlechtes DB-Design:
Ich habe leider noch nicht so viel erfahrung mit DB-design und frage dich daher:Ich habe Viele Werkstoffe.
Einer enthält einige Eigenschaften aber auch viel Elemente.
Die Eigenschaften enthalten jeweils nur einen Eintrag, deswegen können sie mit der Werkstoffnummer in eine Tabelle. Jetzt kommt der Knackpunkt:Die Elemente haben jeweils ein MIN und MAX, also somit 2 Eigenschaften. Da in einer Tabelle eine Eigenschaft nur jeweils eine Spalte besitzen kann, weiss ich nicht wie ich das anders entwerfen soll.
Aber DANKE nochmal für eure schnelle HILFE!!!!!
-
Gib mal ein konkretes Beispiel, bin mir nicht sicher ob ich das jetzt richtig verstehe.
Eine Sache gleich vorweg: du solltest niemals "SELECT * ..." verwenden wenn du über einen Spaltenindex zugreifen willst. Sonst funktioniert der Code nichtmehr richtig wenn du mal irgendwo Spalten in der Mitte einfügst, umordnest oder rauslöscht.
Und wenn ich richtig rate dann könnte eine Tabelle die aus den Spalten Werkstoff_ID, Element_ID, MinCount und MaxCount besteht die bessere Lösung sein.
Da drinnen hast du dann eine Zeile pro Werkstoff und pro Element!
Beispiel:Tabelle "Werkstoff_Elemente" (oder so) Werkstoff_ID Element_ID MinCount MaxCount a x 1 2 a y 0 99 a z 3 3 b x 5 5 c y 7 1000 c z 0 1 ...
Dann reicht ein einziges "SELECT ... FROM Werkstoff_Elemente WHERE Werkstoff_ID = 'a'" aus um an alle "Elemente" dranzukommen. Und vor allem brauchst du nicht 100 verschiedene Tabellen die eigentlich alle dasselbe machen.