views:

451

answers:

3

I'm developing an app in Visual C++ that uses an SQLite3 DB for storing data. Usually it sits in the tray most of the time.

I also would like to enable putting my app in a DropBox folder to share it across several PCs. It worked really well up until DropBox has recently updated itself. And now it says that it "can't sync the file in use". The SQLite file is open in my app, but the lock is shared. There are some prepared statements, but all are reset immediately after using step.

Is there any way to enable synchronizing of an open SQLite database file? Thanks!

Here is the simple wrapper that I use just for testing (no error handling), in case this helps:

 class Statement
 {
 private:
  Statement(sqlite3* db, const std::wstring& sql) : db(db)
  {
   sqlite3_prepare16_v2(db, sql.c_str(), sql.length() * sizeof(wchar_t), &stmt, NULL);
  }

 public:
  ~Statement() { sqlite3_finalize(stmt); }

 public:
  void reset() { sqlite3_reset(stmt); }
  int step() { return sqlite3_step(stmt); }
  int getInt(int i) const { return sqlite3_column_int(stmt, i); }

  std::wstring getText(int i) const
  {
   const wchar_t* v = (const wchar_t*)sqlite3_column_text16(stmt, i);
   int sz = sqlite3_column_bytes16(stmt, i) / sizeof(wchar_t);
   return std::wstring(v, v + sz);
  }

 private:
  friend class Database;

  sqlite3* db;
  sqlite3_stmt* stmt;
 };


 class Database
 {
 public:
  Database(const std::wstring& filename = L"")) : db(NULL)
  {
   sqlite3_open16(filename.c_str(), &db);
  }

  ~Database() { sqlite3_close(db); } 

  void exec(const std::wstring& sql)
  {
   auto_ptr<Statement> st(prepare(sql));
   st->step();   
  }

  auto_ptr<Statement> prepare(const std::wstring& sql) const
  {
   return auto_ptr<Statement>(new Statement(db, sql));
  }

 private:
  sqlite3* db;
 };

UPD: Tried commenting out all calls to LockFile and LockFileEx in sqlite3.c - same result.

UPD2: Tried to call sqlite3_close when idle (just as proof of concept) - still same result! Filemon tells that file is still not closed, only unlocked.

UPD3: Autocommit mode is on. The numbers of BEGINs and COMMITs match (class Transaction and RAII take care of that). SQliteManager is able to connect to the DB while my app is running and make modifications to it.

+2  A: 

What file system are you using?

Are you sure autocommit is on and/or that you are committing your statements? I remember having an issue with not committing and the lock would stay on.

nowarninglabel
NTFS. Good idea about committing, thanks, I'll check if this might be the case.
Alex Jenter
Autocommit mode is on. The numbers of BEGINs and COMMITs match (class Transaction and RAII take care of that). Seems this is not the reason. SQliteManager is able to connect to the DB while my app is running and make modifications to it.
Alex Jenter
+1  A: 

Alex Pechnikov has a multi-master SQLite database replication program sqlite3-rdiff. It is probably overkill for what you are trying to accomplish, but it may be easier than file replication.

SQLite also has an Online Backup API; there's an example on that page: Example 2: Online Backup of a Running Database.

Doug Currie
I can't think of a way how I could use that automatically. sqlite3-rdiff requires Tcl, and using the backup API means making a copy of the whole DB after every change.
Alex Jenter
Isn't DropBox making a copy (or an rdiff) of the DB after every change? If so, a local copy is not much overhead. If not, then only do the local copy as ofter as DropBox needs it.
Doug Currie
+2  A: 

Checking the result from sqlite3_close(). Perhaps it is not working because you have not finalized all your prepared statements.

Doug Currie