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.


Anmelden zum Antworten