Hello again,
Attached is a test application which replicates the problem.
I expected the transactions to block each other exactly like they do in
the beginning (one connection successfully begins and the other receives
SQLITE_BUSY), but I didn't expect the blocked connection to never get
unlocked in the end. What's holding the lock on the database so that the
"begin" can't proceed?
Sample console output:
sqlite3_libversion: 3.7.13
sqlite3_sourceid: 2012-06-11 02:05:22
f5b5a13f7394dc143aa136f1d4faba6839eaa6dc
sqlite3_libversion_number: 3007013
sqlite3_threadsafe: 1
Creating thread 0
Creating thread 1
conn addr status query
--------- ------ -----
0x6a6278 Success PRAGMA journal_mode=wal;
0x6a6278 Success BEGIN IMMEDIATE TRANSACTION;
0x6a6278 Success INSERT INTO test_table_1 (test1, test2)
VALUES ( 1, 2);
0x6a6278 Success COMMIT;
0x6a6278 Success BEGIN IMMEDIATE TRANSACTION;
0x6a6278 Success INSERT INTO test_table_1 (test1, test2)
VALUES ( 1, 2);
0x6a6278 Success COMMIT;
0x6a6278 Success BEGIN IMMEDIATE TRANSACTION;
0x6a6278 Success INSERT INTO test_table_1 (test1, test2)
VALUES ( 1, 2);
0x6bd678 Success PRAGMA journal_mode=wal;
0x6bd678 Failed BEGIN IMMEDIATE TRANSACTION; (return code: 5)
0x6a6278 Success COMMIT;
0x6a6278 Success BEGIN IMMEDIATE TRANSACTION;
0x6a6278 Success INSERT INTO test_table_1 (test1, test2)
VALUES ( 1, 2);
0x6a6278 Success COMMIT;
0x6a6278 Success BEGIN IMMEDIATE TRANSACTION;
0x6a6278 Success INSERT INTO test_table_1 (test1, test2)
VALUES ( 1, 2);
0x6a6278 Success COMMIT;
0x6a6278 Success BEGIN IMMEDIATE TRANSACTION;
0x6a6278 Success INSERT INTO test_table_1 (test1, test2)
VALUES ( 1, 2);
0x6a6278 Success COMMIT;
0x6a6278 Success BEGIN IMMEDIATE TRANSACTION;
0x6a6278 Success INSERT INTO test_table_1 (test1, test2)
VALUES ( 1, 2);
0x6a6278 Success COMMIT;
0x6a6278 Success BEGIN IMMEDIATE TRANSACTION;
0x6a6278 Success INSERT INTO test_table_1 (test1, test2)
VALUES ( 1, 2);
0x6a6278 Success COMMIT;
0x6a6278 Success BEGIN IMMEDIATE TRANSACTION;
0x6a6278 Success INSERT INTO test_table_1 (test1, test2)
VALUES ( 1, 2);
0x6a6278 Success COMMIT;
0x6a6278 Success BEGIN IMMEDIATE TRANSACTION;
0x6a6278 Success INSERT INTO test_table_1 (test1, test2)
VALUES ( 1, 2);
0x6a6278 Success COMMIT;
0x6bd678 Failed BEGIN IMMEDIATE TRANSACTION; (return code: 5)
0x6bd678 Failed BEGIN IMMEDIATE TRANSACTION; (return code: 5)
(... And so on)
Thank you in advance,
Daniel
#include <iostream>
#include <sstream>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include "sqlite3.h"
#define DATABASE_PATH "/tmp/test.db"
#define THREAD_COUNT 2
struct thread_data
{
int thread_id;
};
struct thread_data thread_data_array[THREAD_COUNT];
void execSQL(sqlite3* db, std::string query){
bool keep_trying = true;
while(keep_trying){
char* szError=0;
int return_code = sqlite3_exec(db, query.c_str(), 0, 0,
&szError);
if(return_code == SQLITE_OK){
std::cout<<db<<"\tSuccess\t\t"<<query<<std::endl;
keep_trying = false;
}
else{
std::cerr<<db<<"\tFailed\t\t"<<query<<" (return code:
"<<return_code<<")"<<std::endl;
sleep(1);
}
}
}
void processSQLtest(sqlite3* db, int somedata){
std::stringstream ssInfo;
ssInfo<<" SELECT test_table_2.test1, test_table_2.test2";
ssInfo<<" FROM test_table_2, test_table_3 ";
ssInfo<<" WHERE test_table_2.test1 = test_table_3.test1";
ssInfo<<" AND test_table_3.test2 = "<<somedata;
ssInfo<<" LIMIT 1";
const char* szTail=0;
sqlite3_stmt* pVM;
int nRet = sqlite3_prepare(db, ssInfo.str().c_str(), -1, &pVM, &szTail);
if (nRet != SQLITE_OK)
{
exit(-1);
}
// std::cout<<db<<" Prepared: "<<ssInfo.str()<<std::endl;
nRet = sqlite3_step(pVM);
if (nRet == SQLITE_DONE)
{
// no rows
}
else if (nRet == SQLITE_ROW)
{
// at least 1 row
//insert some data
for(unsigned int i = 0; i < 10; i++){
execSQL(db, "BEGIN IMMEDIATE TRANSACTION;");
execSQL(db, "INSERT INTO test_table_1 (test1, test2)
VALUES ( 1, 2);");
execSQL(db, "COMMIT;");
}
}
else
{
nRet = sqlite3_finalize(pVM);
}
}
void *dbThread(void *threadarg)
{
sleep(1);
struct thread_data *my_data;
my_data = (struct thread_data *) threadarg;
sqlite3* db;
int nRet = sqlite3_open(DATABASE_PATH, &db);
if (nRet != SQLITE_OK)
{
//const char* szError = sqlite3_errmsg(db);
}else{
execSQL(db, "PRAGMA journal_mode=wal;");
//fake a thread processing loop using the opened database
connection
bool sql_processed = false;
while(1){
if(!sql_processed){
processSQLtest(db, my_data->thread_id+1);
sql_processed = true;
}else{
sleep(1);
}
}
//Close the db if the processing loop is done (which won't
happen in this example)
sqlite3_close(db);
}
std::cout<<"Thread "<<my_data->thread_id<<" exiting"<<std::endl;
pthread_exit(NULL);
return NULL;
}
void recreateDB(){
sqlite3* temp_db;
int nRet = sqlite3_open(DATABASE_PATH, &temp_db);
if (nRet != SQLITE_OK)
{
std::cout<<"Unable to open database"<<std::endl;
exit(0);
}
sqlite3_busy_timeout(temp_db, 10000);
std::string CreateTables;
CreateTables.append("DROP TABLE IF EXISTS test_table_1;");
CreateTables.append("DROP TABLE IF EXISTS test_table_2;");
CreateTables.append("DROP TABLE IF EXISTS test_table_3;");
CreateTables.append(" CREATE TABLE test_table_1 (");
CreateTables.append(" test1 INT NOT NULL, ");
CreateTables.append(" test2 INT NOT NULL ");
CreateTables.append(" );");
CreateTables.append(" CREATE TABLE test_table_2 (");
CreateTables.append(" test1 INT NOT NULL, ");
CreateTables.append(" test2 INT NOT NULL ");
CreateTables.append(" );");
CreateTables.append(" CREATE TABLE test_table_3 (");
CreateTables.append(" test1 INT NOT NULL, ");
CreateTables.append(" test2 INT NOT NULL ");
CreateTables.append(");");
char* szError=0;
sqlite3_exec(temp_db, CreateTables.c_str(), 0, 0, &szError);
sqlite3_exec(temp_db, "INSERT INTO test_table_2 VALUES(1, 1);", 0, 0,
&szError);
sqlite3_exec(temp_db, "INSERT INTO test_table_2 VALUES(2, 2);", 0, 0,
&szError);
sqlite3_exec(temp_db, "INSERT INTO test_table_2 VALUES(3, 3);", 0, 0,
&szError);
sqlite3_exec(temp_db, "INSERT INTO test_table_3 VALUES(1, 1);", 0, 0,
&szError);
sqlite3_exec(temp_db, "INSERT INTO test_table_3 VALUES(1, 2);", 0, 0,
&szError);
sqlite3_exec(temp_db, "INSERT INTO test_table_3 VALUES(1, 3);", 0, 0,
&szError);
sqlite3_close(temp_db);
}
int main(int argc, char*argv[]){
std::cout<<"sqlite3_libversion: "<<sqlite3_libversion()<<std::endl;
std::cout<<"sqlite3_sourceid: "<<sqlite3_sourceid()<<std::endl;
std::cout<<"sqlite3_libversion_number:
"<<sqlite3_libversion_number()<<std::endl;
std::cout<<"sqlite3_threadsafe: "<<sqlite3_threadsafe()<<std::endl;
recreateDB();
pthread_t threads[THREAD_COUNT];
int rc;
unsigned int t;
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
for(t=0;t<THREAD_COUNT;t++) {
thread_data_array[t].thread_id = t;
std::cout<<"Creating thread "<<t<<std::endl;
rc = pthread_create(&threads[t], &attr, dbThread, (void
*)&thread_data_array[t]);
if (rc) {
std::cout<<"ERROR; return code from pthread_create() is
"<<rc<<std::endl;
exit(-1);
}
}
std::cout<<"\nconn addr\tstatus\t\tquery"<<std::endl;
std::cout<<"---------\t------\t\t-----"<<std::endl;
while(1){
sleep(1);
}
}
_______________________________________________
sqlite-users mailing list
sqlite-users@sqlite.org
http://sqlite.org:8080/cgi-bin/mailman/listinfo/sqlite-users