Skip to content

Commit

Permalink
- tweaked the sync dialog and the table therein to look a little more…
Browse files Browse the repository at this point in the history
… suitable, instead of brutally clipped off.

- added the Sync Target column at the right size in there, so you can see what Qiqqa thinks the current Sync Target directory might be. `(null)` when none has been set up.
- WARNING: this stuff is not yet editable!
- File.Copy() gives out a horrible user-unfriendly exception: path only, no cause, no blurb about it being the source or target either. Hackily fixed for the sync template database now. Must be inspected. MIGHT be just good enough for recovering from a broken sync target database like Simon Dedman in jimmejardine#257 is possibly sadled with. (IFF one goes and *manually* deletes the corrupted XYZ.s3db database file in that sync destination, anyway. Dangerous stuff. :-)
  • Loading branch information
GerHobbelt committed Oct 29, 2020
1 parent 0f76cd5 commit 779b6e1
Show file tree
Hide file tree
Showing 5 changed files with 387 additions and 255 deletions.
271 changes: 156 additions & 115 deletions Qiqqa/DocumentLibrary/IntranetLibraryStuff/IntranetLibraryDB.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,24 @@ public IntranetLibraryDB(string base_path)
{
Logging.Warn("Intranet Library metadata db does not exist so copying the template to {0}", library_path);
string library_metadata_template_path = Path.GetFullPath(Path.Combine(ConfigurationManager.Instance.StartupDirectoryForQiqqa, @"DocumentLibrary/IntranetLibraryStuff/IntranetLibrary.Metadata.Template.s3db"));
File.Copy(library_metadata_template_path, library_path);
if (!File.Exists(library_metadata_template_path))
{
throw new Exception($"Sync template file '{library_metadata_template_path}' does not exist!");
}
string basedir = Path.GetDirectoryName(library_path);
if (!Directory.Exists(basedir))
{
throw new Exception($"Sync target directory '{basedir}' for Qiqqa database '{library_path}' does not exist!");
}
try
{
File.Copy(library_metadata_template_path, library_path);
}
catch (Exception ex)
{
Logging.Error(ex, "Error 0x{2:08X}: Failed to write the sync template '{0}' to sync target directory '{1}'", library_metadata_template_path, basedir, ex.HResult);
throw ex;
}
}
}

Expand Down Expand Up @@ -93,59 +110,67 @@ public void PutBlob(string filename, byte[] data)
// Calculate the MD5 of this blobbiiiieeeeee
string md5 = StreamMD5.FromBytes(data);

lock (DBAccessLock.db_access_lock)
try
{
using (var connection = GetConnection())
lock (DBAccessLock.db_access_lock)
{
connection.Open();
using (var transaction = connection.BeginTransaction())
using (var connection = GetConnection())
{
bool managed_update = false;

using (var command = new SQLiteCommand("UPDATE LibraryItem SET MD5=@md5, DATA=@data, LAST_UPDATED_BY=@last_updated_by WHERE filename=@filename", connection, transaction))
connection.Open();
using (var transaction = connection.BeginTransaction())
{
command.Parameters.AddWithValue("@filename", filename);
command.Parameters.AddWithValue("@last_updated_by", Environment.UserName);
command.Parameters.AddWithValue("@md5", md5);
command.Parameters.AddWithValue("@data", data);
int num_rows_updated = command.ExecuteNonQuery();
if (1 == num_rows_updated)
{
managed_update = true;
}
}
bool managed_update = false;

if (!managed_update)
{
using (var command = new SQLiteCommand("INSERT INTO LibraryItem(filename, last_updated_by, md5, data) VALUES(@filename, @last_updated_by, @md5, @data)", connection, transaction))
using (var command = new SQLiteCommand("UPDATE LibraryItem SET MD5=@md5, DATA=@data, LAST_UPDATED_BY=@last_updated_by WHERE filename=@filename", connection, transaction))
{
command.Parameters.AddWithValue("@filename", filename);
command.Parameters.AddWithValue("@last_updated_by", Environment.UserName);
command.Parameters.AddWithValue("@md5", md5);
command.Parameters.AddWithValue("@data", data);
command.ExecuteNonQuery();
int num_rows_updated = command.ExecuteNonQuery();
if (1 == num_rows_updated)
{
managed_update = true;
}
}

if (!managed_update)
{
using (var command = new SQLiteCommand("INSERT INTO LibraryItem(filename, last_updated_by, md5, data) VALUES(@filename, @last_updated_by, @md5, @data)", connection, transaction))
{
command.Parameters.AddWithValue("@filename", filename);
command.Parameters.AddWithValue("@last_updated_by", Environment.UserName);
command.Parameters.AddWithValue("@md5", md5);
command.Parameters.AddWithValue("@data", data);
command.ExecuteNonQuery();
}
}
}

transaction.Commit();
transaction.Commit();
}
connection.Close();
}
connection.Close();
}

//
// see SO link in ../LibraryDB.cs at the `DBAccessLock.db_access_lock` declaration.
//
// We keep this *inside* the critical section so that we know we'll be the only active SQLite
// action which just transpired.
// *This* is also the reason why I went with a *global* lock (singeton) for *all* databases,
// even while *theoretically* this is *wrong* or rather: *unneccessary* as the databases
// i.e. Qiqqa Libraries shouldn't bite one another. I, however, need to ensure that the
// added `System.Data.SQLite.SQLiteConnection.ClearAllPools();` statements don't foul up
// matters in another library while lib A I/O is getting cleaned up.
//
// In short: Yuck. + Cave canem.
//
SQLiteConnection.ClearAllPools();
//
// see SO link in ../LibraryDB.cs at the `DBAccessLock.db_access_lock` declaration.
//
// We keep this *inside* the critical section so that we know we'll be the only active SQLite
// action which just transpired.
// *This* is also the reason why I went with a *global* lock (singleton) for *all* databases,
// even while *theoretically* this is *wrong* or rather: *unnecessary* as the databases
// i.e. Qiqqa Libraries shouldn't bite one another. I, however, need to ensure that the
// added `System.Data.SQLite.SQLiteConnection.ClearAllPools();` statements don't foul up
// matters in library B while lib A I/O is getting cleaned up.
//
// In short: Yuck. + Cave canem.
//
SQLiteConnection.ClearAllPools();
}
}
catch (Exception ex)
{
Logging.Error(ex, "IntranetLibraryDB::PutBLOB: Database I/O failure for DB '{0}'.", library_path);
throw ex;
}
}

Expand Down Expand Up @@ -187,69 +212,77 @@ public List<IntranetLibraryItem> GetIntranetLibraryItems(string filename, int Ma
{
List<IntranetLibraryItem> results = new List<IntranetLibraryItem>();

lock (DBAccessLock.db_access_lock)
try
{
using (var connection = GetConnection())
lock (DBAccessLock.db_access_lock)
{
connection.Open();

string command_string = "SELECT filename, last_updated_by, md5, data FROM LibraryItem WHERE 1=1 ";
command_string += turnArgumentIntoQueryPart("filename", filename);
if (MaxRecordCount > 0)
using (var connection = GetConnection())
{
// http://www.sqlitetutorial.net/sqlite-limit/
command_string += " LIMIT @maxnum";
}
connection.Open();

using (var command = new SQLiteCommand(command_string, connection))
{
turnArgumentIntoQueryParameter(command, "filename", filename);
string command_string = "SELECT filename, last_updated_by, md5, data FROM LibraryItem WHERE 1=1 ";
command_string += turnArgumentIntoQueryPart("filename", filename);
if (MaxRecordCount > 0)
{
command.Parameters.AddWithValue("@maxnum", MaxRecordCount);
// http://www.sqlitetutorial.net/sqlite-limit/
command_string += " LIMIT @maxnum";
}

using (SQLiteDataReader reader = command.ExecuteReader())
using (var command = new SQLiteCommand(command_string, connection))
{
while (reader.Read())
turnArgumentIntoQueryParameter(command, "filename", filename);
if (MaxRecordCount > 0)
{
IntranetLibraryItem result = new IntranetLibraryItem();
results.Add(result);

result.filename = reader.GetString(0);
result.last_updated_by = reader.GetString(1);
result.md5 = reader.GetString(2);
command.Parameters.AddWithValue("@maxnum", MaxRecordCount);
}

long total_bytes = reader.GetBytes(3, 0, null, 0, 0);
result.data = new byte[total_bytes];
long total_bytes2 = reader.GetBytes(3, 0, result.data, 0, (int)total_bytes);
if (total_bytes != total_bytes2)
using (SQLiteDataReader reader = command.ExecuteReader())
{
while (reader.Read())
{
throw new Exception("Error reading blob - blob size different on each occasion.");
IntranetLibraryItem result = new IntranetLibraryItem();
results.Add(result);

result.filename = reader.GetString(0);
result.last_updated_by = reader.GetString(1);
result.md5 = reader.GetString(2);

long total_bytes = reader.GetBytes(3, 0, null, 0, 0);
result.data = new byte[total_bytes];
long total_bytes2 = reader.GetBytes(3, 0, result.data, 0, (int)total_bytes);
if (total_bytes != total_bytes2)
{
throw new Exception("Error reading blob - blob size different on each occasion.");
}
}
}

reader.Close();
reader.Close();
}
}

connection.Close();
}

connection.Close();
//
// see SO link above at the `DBAccessLock.db_access_lock` declaration.
//
// We keep this *inside* the critical section so that we know we'll be the only active SQLite
// action which just transpired.
// *This* is also the reason why I went with a *global* lock (singleton) for *all* databases,
// even while *theoretically* this is *wrong* or rather: *unnecessary* as the databases
// i.e. Qiqqa Libraries shouldn't bite one another. I, however, need to ensure that the
// added `System.Data.SQLite.SQLiteConnection.ClearAllPools();` statements don't foul up
// matters in library B while lib A I/O is getting cleaned up.
//
// In short: Yuck. + Cave canem.
//
SQLiteConnection.ClearAllPools();
}

//
// see SO link above at the `DBAccessLock.db_access_lock` declaration.
//
// We keep this *inside* the critical section so that we know we'll be the only active SQLite
// action which just transpired.
// *This* is also the reason why I went with a *global* lock (singeton) for *all* databases,
// even while *theoretically* this is *wrong* or rather: *unneccessary* as the databases
// i.e. Qiqqa Libraries shouldn't bite one another. I, however, need to ensure that the
// added `System.Data.SQLite.SQLiteConnection.ClearAllPools();` statements don't foul up
// matters in another library while lib A I/O is getting cleaned up.
//
// In short: Yuck. + Cave canem.
//
SQLiteConnection.ClearAllPools();
}
catch (Exception ex)
{
Logging.Error(ex, "IntranetLibraryDB::GetLibraryItems: Database I/O failure for DB '{0}'.", library_path);
throw ex;
}

return results;
Expand All @@ -259,48 +292,56 @@ public List<IntranetLibraryItem> GetIntranetLibraryItemsSummary()
{
List<IntranetLibraryItem> results = new List<IntranetLibraryItem>();

lock (DBAccessLock.db_access_lock)
try
{
using (var connection = GetConnection())
lock (DBAccessLock.db_access_lock)
{
connection.Open();
using (var connection = GetConnection())
{
connection.Open();

string command_string = "SELECT filename, md5 FROM LibraryItem WHERE 1=1 ";
string command_string = "SELECT filename, md5 FROM LibraryItem WHERE 1=1 ";

using (var command = new SQLiteCommand(command_string, connection))
{
using (SQLiteDataReader reader = command.ExecuteReader())
using (var command = new SQLiteCommand(command_string, connection))
{
while (reader.Read())
using (SQLiteDataReader reader = command.ExecuteReader())
{
IntranetLibraryItem result = new IntranetLibraryItem();
results.Add(result);
while (reader.Read())
{
IntranetLibraryItem result = new IntranetLibraryItem();
results.Add(result);

result.filename = reader.GetString(0);
result.md5 = reader.GetString(1);
}
result.filename = reader.GetString(0);
result.md5 = reader.GetString(1);
}

reader.Close();
reader.Close();
}
}

connection.Close();
}

connection.Close();
//
// see SO link above at the `DBAccessLock.db_access_lock` declaration.
//
// We keep this *inside* the critical section so that we know we'll be the only active SQLite
// action which just transpired.
// *This* is also the reason why I went with a *global* lock (singleton) for *all* databases,
// even while *theoretically* this is *wrong* or rather: *unnecessary* as the databases
// i.e. Qiqqa Libraries shouldn't bite one another. I, however, need to ensure that the
// added `System.Data.SQLite.SQLiteConnection.ClearAllPools();` statements don't foul up
// matters in library B while lib A I/O is getting cleaned up.
//
// In short: Yuck. + Cave canem.
//
SQLiteConnection.ClearAllPools();
}

//
// see SO link above at the `DBAccessLock.db_access_lock` declaration.
//
// We keep this *inside* the critical section so that we know we'll be the only active SQLite
// action which just transpired.
// *This* is also the reason why I went with a *global* lock (singeton) for *all* databases,
// even while *theoretically* this is *wrong* or rather: *unneccessary* as the databases
// i.e. Qiqqa Libraries shouldn't bite one another. I, however, need to ensure that the
// added `System.Data.SQLite.SQLiteConnection.ClearAllPools();` statements don't foul up
// matters in another library while lib A I/O is getting cleaned up.
//
// In short: Yuck. + Cave canem.
//
SQLiteConnection.ClearAllPools();
}
catch (Exception ex)
{
Logging.Error(ex, "IntranetLibraryDB::GetItemsSummary: Database I/O failure for DB '{0}'.", library_path);
throw ex;
}

return results;
Expand Down
Loading

0 comments on commit 779b6e1

Please sign in to comment.