SQlite3 error: database is locked



  • Hallo,
    bin jetzt schon seit 3 Tagen daran eine Funktion zu erstellen, mit der ich Tabellen aus dem Vorjahr in eine neue db übernehmen kann. Leider ziemlich erfolglos...

    Ich möchte also für jedes Jahr eine db anlegen, die auch so heißt. Z.B. 2021.s3db.
    Immer wenn ein neues Jahr beginnt, wird eine neue db angelegt.
    Jetzt möchte ich einige Tabellen aus dem Vorjahr übernehmen, damit man nicht jedes
    Jahr die Konten neu anlegen muss.

    Prinzipiell gehe ich so vor:

    • Neue DB anlegen
    • Tabellenstruktur erzeugen
    • Neue DB öffnen
    • Über attach die alte DB hinzufügen // Bis hier hin funktioniert auch alles
    • Tabelleninhalte von alt nach neu übernehmen.
      Leider gibt es oben genannten error, sobald ich versuche die Daten zu übernehmen.
      Dabei ist mir völlig unklar, warum die db locked ist.

    Ich hänge hier mal ein bisschen Code an. Ist zwar durch meine Versuche noch etwas "verunreinigt", aber das sollte jetzt nicht weiter stören....

    void TFormMain::CopyTables()
    {
        sqlite3 *db;
      	char *zErrMsg = 0;
      	int rc;
        string tmp;
        ostringstream os;
    
        os << AktJahr << ".s3db";
    
      	tmp = os.str();
        vector<char> num_char(tmp.begin(), tmp.end());
      	num_char.push_back('\0');
        rc = sqlite3_open(&num_char[0], &db);         // Datenbank öffnen
    
        if( rc )
       	{
            /*
        	os << "Can't open database: " << sqlite3_errmsg(db) << "\n";
        	tmp = os.str();
        	vector<char> str(tmp.begin(), tmp.end());
      		str.push_back('\0');
            ShowMessage(&str[0]);
            sqlite3_free(zErrMsg);
            */
      	}
      	else
      	{
            /*
        	os << "Opened database successfully\n";
        	tmp = os.str();
        	vector<char> str(tmp.begin(), tmp.end());
      		str.push_back('\0');
            ShowMessage(&str[0]);
            */
      	}
        os.str("");  	// reset the string to be empty
    	os.clear();     // clear any error flags that may be set
        os << "ATTACH DATABASE '" << AktJahr-1 << ".s3db' AS old_db;";
    
        tmp = os.str();
        vector<char> sql_query(tmp.begin(), tmp.end());
        sql_query.push_back('\0');
        rc = sqlite3_exec(db, &sql_query[0], callback, 0, &zErrMsg);
    
        if( rc != SQLITE_OK )
        {
           	os << "SQL error: " << zErrMsg <<"\n";
           	tmp = os.str();
           	vector<char> str(tmp.begin(), tmp.end());
      		str.push_back('\0');
           	//ShowMessage(&str[0]);
           	sqlite3_free(zErrMsg);
        }
        else
        {
           	os << "Records created successfully\n";
           	tmp = os.str();
           	vector<char> str(tmp.begin(), tmp.end());
      		str.push_back('\0');
           	//ShowMessage(&str[0]);
        }
    
    
    	for(int i = 0; i < 2; i++)
    	{
            os.str("");  	// reset the string to be empty
    		os.clear();     // clear any error flags that may be set
    
          	switch(i)
            {
            case 0:
                    // os << "PRAGMA database_list;";
                    os << "PRAGMA locking_mode = NORMAL;";     // NORMAL / EXCLUSIVE
                    break;
            case 1:
                    os << "INSERT INTO main.AusgHauptgruppe SELECT * FROM old_db.AusgHauptgruppe;";
                    //os << "INSERT INTO main.AusgHauptgruppe(Position, Gruppe, Saldo) "
                    //	<< "SELECT Position, Gruppe, Saldo FROM old_db.AusgHauptgruppe;";
                    break;
            case 2:
                    os << "INSERT INTO main.AusgKto1(Position, Konto) "
                    	<< "SELECT Position, Konto FROM old_db.AusgKto1;";
                    break;
        	case 3:
                    os << "INSERT INTO main.AusgKto2(Position, Konto) "
                    	<< "SELECT Position, Konto FROM old_db.AusgKto2;";
                    break;
            case 4:
                    os << "INSERT INTO main.AusgKto3(Position, Konto) "
                    	<< "SELECT Position, Konto FROM old_db.AusgKto3;";
                    break;
            case 5:
                    os << "INSERT INTO main.AusgKto4(Position, Konto) "
                    	<< "SELECT Position, Konto FROM old_db.AusgKto4;";
                    break;
            case 6:
                    os << "INSERT INTO main.AusgKto5(Position, Konto) "
                    	<< "SELECT Position, Konto FROM old_db.AusgKto5;";
                    break;
            case 7:
                    os << "INSERT INTO main.AusgKto6(Position, Konto) "
                    	<< "SELECT Position, Konto FROM old_db.AusgKto6;";
                    break;
            case 8:
                    os << "INSERT INTO main.AusgKto7(Position, Konto) "
                    	<< "SELECT Position, Konto FROM old_db.AusgKto7;";
                    break;
            case 9:
                    os << "INSERT INTO main.AusgKto8(Position, Konto) "
                    	<< "SELECT Position, Konto FROM old_db.AusgKto8;";
                    break;
            case 10:
                    os << "INSERT INTO main.EinKonten(Position, Konto, Saldo) "
                    	<< "SELECT Position, Konto, Saldo FROM old_db.EinKonten;";
                    break;
    	    case 11:
                    os << "INSERT INTO main.Kontenliste(Position, Konto, Saldo) "
                    	<< "SELECT Position, Konto, Saldo FROM old_db.Kontenliste;";
                    break;
            case 12:
                    os << "INSERT INTO main.Zahlart(Position, Text, vonKto, mitNr) "
                    	<< "SELECT Position, Text, vonKto, mitNr FROM old_db.Zahlart;";
                    break;
            case 13:
            		os << "DETACH DATABASE '" << AktJahr-1 << ".s3db';";
                    break;
            default:
            		break;
        	}
    
    
            tmp = os.str();
        	vector<char> sql_query(tmp.begin(), tmp.end());
        	sql_query.push_back('\0');
            rc = sqlite3_exec(db, &sql_query[0], callback, 0, &zErrMsg);
    
            os.str("");  	// reset the string to be empty
    		os.clear();     // clear any error flags that may be set
            if( rc != SQLITE_OK )
          	{
            	os << "SQL error: " << zErrMsg <<"\n";
            	tmp = os.str();
            	vector<char> str(tmp.begin(), tmp.end());
      			str.push_back('\0');
    
            	ShowMessage(&str[0]);
                sqlite3_free(zErrMsg);
          	}
          	else
         	{
            	os << "Records created successfully\n";
            	tmp = os.str();
            	vector<char> str(tmp.begin(), tmp.end());
      			str.push_back('\0');
    
            	ShowMessage(&str[0]);
           	}
        }
        sqlite3_close(db);
    }
    //---------------------------------------------------------------------------
    
    static int callback(void *NotUsed, int col, char **row, char **azColName)
    {
        char str[128];
        memset(&str[0], 0, 128);
    
        for(int i = 0; i < col; i++)
       	{
          	strcat(&str[0], row[i] ? row[i] : "NULL");
       	}
       	ShowMessage(&str[0]);
     	return 0;
    }
    

    Viele Grüße
    Matthias



  • Keine Ahnung, warum Deine DB locked ist, das musst Du schon selbst rausfinden ...
    Aber:
    Du kannst doch Dein Ziel auch einfach erreichen, indem Du die ganze DB kopierst:
    copy 2020.s3db 2021.s3db
    und dann in 2021.s3db alles löschst, was Du nicht mehr haben willst, dann brauchst Du die alte DB für diesen Vorgang gar nicht mehr?

    PS
    Du öffnest die neue DB ja in Deiner Funktion oben, ich nehme an, die Tabellenstruktur hast Du an anderer Stelle bereits angelegt, in demselben Programm?
    Falls ja, hast Du die DB dort wieder geschlossen?
    Nicht, dass Du sie selbst lockst ...



  • @Belli
    Falls ja, hast Du die DB dort wieder geschlossen?

    Ups 😳

    Da hast du mich auf etwas gestoßen... Tatsächlich gibt es für normale Zugriffe ein ganz anderes Handling. Nur, weil in diesem speziellen Fall die Connection offen bleiben muss, habe ich diese Ausnahme geschrieben.

    Und in dem anderen Handling wird tatsächlich die DB geöffnet und nicht wieder geschlossen. Da hast einen Bug gefunden 😄

    Jetzt bin ich schonmal einen Schritt weiter. Jetzt kommt der Fehler: Tabelle old_db.AusgHauptgruppe existiert nicht. Das halte ich für ein Gerücht!

    Vielleicht ein Problem in der Syntax?



  • OT: std::string hat eine c_str Funktion. Deine hampelei mit std::vector ist überflüssig.



  • @MHage warum erzeugst du für jedes Jahr eine neue Datenbank, statt die Daten mit einem Zeitstempel zu versehen?



  • @manni66
    string hat eine c_str Funktion.... Ja, das ist mir bekannt.
    Stehe dennoch gerade etwas auf dem Schlauch. Wie würdest du es schreiben? Kurzes Beispiel wär nett.

    Tatsächlich haben die Daten einen Zeitstempel. Jedes Jahr eine neue Datendatei anzulegen ist zugegebenermaßen nicht wirklich üblich und meistens wenig Sinnvoll.
    In diesem Fall soll es dem Benutzer die Datensicherung etwas erleichtern. Ich weiß, das es dafür wesentlich elegantere Methoden gibt....



  • @MHage sagte in SQlite3 error: database is locked:

    Wie würdest du es schreiben?

    Wie was schreiben? Welches Problem löst Du denn mit Deinem vector?



  • Alles klar... 🙂
    vector brauch ich überhaupt nicht... Ein ewig lernender....



  • @MHage sagte in SQlite3 error: database is locked:

    Jetzt bin ich schonmal einen Schritt weiter. Jetzt kommt der Fehler: Tabelle old_db.AusgHauptgruppe existiert nicht. Das halte ich für ein Gerücht!

    Bist du dir sicher, dass 2020.s3db an dem Pfad an dem gesucht wird, auch gefunden werden kann? Ich würde es mal testweise mit einem absoluten Pfad versuchen. Wenn sqlite 2020.s3db nicht findet, wird eine leere Datenbank erzeugt, in der die Tabelle AusgHauptgruppe natürlich nicht existiert.



  • @Schlangenmensch

    Der Gedanke kam mir auch schon einmal. In meinem Quellcode oben findest du bei case 1:
    folgende Zeile:

    os << "PRAGMA database_list;";

    Im Rückgabewert stand
    0: main KompletterPfad\2021.s3db
    2: old_db KompletterPfad\2020.s3db

    Daher habe ich den Gedanken wieder verworfen. Wundert mich nur, das der Index 2 und nicht 1 ist...
    Übrigens, die Meldung "database locked" kommt immernoch. Irgendwie weiß ich da auch nicht weiter...

    VG



  • Mittlerweile habe ich herausgefunden, das das Problem mit der locked database mit der Syntax des sql_query's zu tun hat

    os << "INSERT INTO main.AusgHauptgruppe(Position, Gruppe, Saldo) "
                    	<< "SELECT 'Position', 'Gruppe', 'Saldo' FROM 'old_db.AusgHauptgruppe';";
    

    Schreibe ich es so wie hier gezeigt, ist die db nicht mehr locked. Dafür bekomme ich jetzt die Meldung, das die Tabelle old_db.AusgHauptgruppe nicht existiert.
    Wenn man den String als ganzes sieht, hat er sogar Recht. Ich denke, aus irgendeinem Grund/ oder meiner Schreibweise ist nicht klar, das ich über old_db eine andere db anspreche....

    Korrekt "attached" ist sie aber, das habe ich kontrolliert.



  • mir fällt auf die Schnelle auf, dass Du bei insert into die Zieltabelle nicht in Hochkomma 'eingefasst hast, die Quelltabelle bei from dann aber schon, ist das so korrekt?



  • Es ist für mich immer wieder irgendwie ein Rätselraten, wie nun genau die Schreibweise auszusehen hat. Grundsätzlich ist erstmal alles, was ich schreibe in Frage zustellen... 🙂

    Ich habe über sie sqlite3.exe aus dem Tools-Archiv das gleiche mal gemacht. Da hat es funktioniert. D.h. weiter database über attach hinzufügen und einzelne Daten aus Tabellen kopieren. Da gab es kein Problem mit locked. Ich habe irgendwie das Gefühl, das die C++ Schnittstelle da eine Besonderheit hat. Sonst kann ich alles, zu jedem Zeitpunkt, egal wie oft, ausführen. Wirklich egal, ob SELECT, INSERT, UPDATE oder DELETE. Noch nicht ein einziges mal Probleme gehabt. Nur jetzt, nach dem hinzufügen einer weiteren db.

    Schon eigenartig. ...



  • Ich würde dann mal in einem Testfall den selben SQL Code über das C Interface ausführen, wie über die Kommandozeile.
    Also, DB öffnen, andere DB attachen, und einen String mit dem Befehl da rein schieben. Alles unnötige drum herum weg, gleiche Schreibweise im C++ Code wie in der Commandozeile.

    Bei genauerem hinsehen ist:

    @MHage sagte in SQlite3 error: database is locked:

    os << "INSERT INTO main.AusgHauptgruppe(Position, Gruppe, Saldo) "
                   	<< "SELECT 'Position', 'Gruppe', 'Saldo' FROM 'old_db.AusgHauptgruppe';";
    

    auch falsch: Du fügst da immer die String "Position", "Gruppe" und "Saldo" ein, wegen der Anführungszeichen. Dafür brauchst du die andere Tabelle überhaupt nicht.

    Versuch mal

    os << "INSERT INTO main.AusgHauptgruppe(Position, Gruppe, Saldo) "
                   	<< "SELECT Position, Gruppe, Saldo FROM old_db.AusgHauptgruppe;";
    


  • @Schlangenmensch
    Ok... neuer Versuch
    Habe jetzt wie folgt umgeschrieben

    void TFormMain::CopyTables()
    {
        sqlite3 *db;
        sqlite3_stmt* stmt = 0;
      	char *zErrMsg = 0;
      	int rc;
        string tmp;
        ostringstream os;
        const char *sql_query;
        char *str;
    
        os << AktJahr << ".s3db";
        tmp = os.str();
        const char *num_char = tmp.c_str();
        rc = sqlite3_open(&num_char[0], &db);         // Datenbank öffnen
    
        for(int i = 0; i < 14; i++)
    	{
            os.str("");  	// reset the string to be empty
    		os.clear();     // clear any error flags that may be set
            stmt = 0;
          	switch(i)
            {
            case 0:
            		os << "ATTACH DATABASE '" << AktJahr-1 << ".s3db' AS old_db;";
                    break;
            case 1:
                    os << "INSERT INTO main.AusgHauptgruppe(Position, Gruppe, Saldo) "
                    	<< "SELECT Position, Gruppe, Saldo FROM old_db.AusgHauptgruppe;";
                    break;
            case 2:
                    os << "INSERT INTO main.AusgKto1(Position, Konto) "
                    	<< "SELECT Position, Konto FROM old_db.AusgKto1;";
                    break;
        	case 3:
                    os << "INSERT INTO main.AusgKto2(Position, Konto) "
                    	<< "SELECT Position, Konto FROM old_db.AusgKto2;";
                    break;
            case 4:
                    os << "INSERT INTO main.AusgKto3(Position, Konto) "
                    	<< "SELECT Position, Konto FROM old_db.AusgKto3;";
                    break;
            case 5:
                    os << "INSERT INTO main.AusgKto4(Position, Konto) "
                    	<< "SELECT Position, Konto FROM old_db.AusgKto4;";
                    break;
            case 6:
                    os << "INSERT INTO main.AusgKto5(Position, Konto) "
                    	<< "SELECT Position, Konto FROM old_db.AusgKto5;";
                    break;
            case 7:
                    os << "INSERT INTO main.AusgKto6(Position, Konto) "
                    	<< "SELECT Position, Konto FROM old_db.AusgKto6;";
                    break;
            case 8:
                    os << "INSERT INTO main.AusgKto7(Position, Konto) "
                    	<< "SELECT Position, Konto FROM old_db.AusgKto7;";
                    break;
            case 9:
                    os << "INSERT INTO main.AusgKto8(Position, Konto) "
                    	<< "SELECT Position, Konto FROM old_db.AusgKto8;";
                    break;
            case 10:
                    os << "INSERT INTO main.EinKonten(Position, Konto, Saldo) "
                    	<< "SELECT Position, Konto, Saldo FROM old_db.EinKonten;";
                    break;
    	    case 11:
                    os << "INSERT INTO main.Kontenliste(Position, Konto, Saldo) "
                    	<< "SELECT Position, Konto, Saldo FROM old_db.Kontenliste;";
                    break;
            case 12:
                    os << "INSERT INTO main.Zahlart(Position, Text, vonKto, mitNr) "
                    	<< "SELECT Position, Text, vonKto, mitNr FROM old_db.Zahlart;";
                    break;
            case 13:
            		os << "DETACH DATABASE '" << AktJahr-1 << ".s3db';";
                    break;
            default:
            		break;
        	}
    
            tmp = os.str();
            sql_query = tmp.c_str();
            rc = sqlite3_prepare_v2( db, &sql_query[0], -1, &stmt, NULL );
            int retVal = sqlite3_step(stmt);
            	if (retVal != SQLITE_DONE)
                {
    
                }
            sqlite3_reset(stmt);
            sqlite3_finalize(stmt);
        }
        sqlite3_exec(db, "COMMIT TRANSACTION", NULL, NULL, &zErrMsg);
        sqlite3_close(db);
    }
    

    Bei case 1: ist der RetVal von sqlite3_step(stmt); 101 SQLITE_DONE. Hier also alles schön!
    Bei case 2: ist der RetVal 5 . Entspricht SQLITE_BUSY

    Sagt mir, das die Ausführung von case 2: nicht richtig abgeschlossen wurde.
    Alles in Allem schon mal ein Schritt in die richtige Richtung. Immerhin nicht mehr locked😉

    Jetzt wäre nur interessant, warum er nicht richtig abgeschlossen hat...

    Nachtrag:
    Habe folgendes direkt nach dem öffnen der db eingefügt

    sqlite3_exec(db, "BEGIN TRANSACTION;", NULL, NULL, NULL);
    

    Das hat jetzt bewirkt, das er immer mit Status 101 abschliesst.
    Leider steht nur nichts in der db. Also nur so getan als ob....
    Vielleicht noch Syntax?



  • BEGIN TRANSACTION und COMMIT TRANSACTION solltest du nicht brauchen. reset und finalize garantieren jeweils, das die implizierte Transaktion abgeschlossen wird. Mit sqlite3_prepare_v2 und finalize sollte auch das reset überflüssig sein 😉

    Aber auch finalize und reset geben Fehlercodes zurück, die man abfragen könnte. Vlt geht schon vorher was schief.

    SQLITE_BUSY spricht eigentlich dafür, dass noch ein anderer Prozess auf die DB zugreift. Hast du vlt dein Programm mehrfach gestartet und hast die DB ausversehen mehrfach geöffnet?



  • @Schlangenmensch
    n'abend,

    Nachdem ich noch einige Stunden an dem Problem gesessen habe, ohne auch nur einen Schritt weiter zu kommen, habe ich mir entschlossen eine kleine Test-App zu machen.

    Denn an meinem aktuellen Stand kann ich keinen Fehler mehr finden...

    Was soll ich sagen, es hat auf anhieb einwandfrei funktioniert. 😲

    Nun gut, in meinem eigentlichen Programm funktioniert es jetzt zwar immer noch nicht, aber nun weiß ich, wo ich weiter suchen muss...

    Vielen Dank für eure Hilfe!




Anmelden zum Antworten