aboutsummaryrefslogtreecommitdiffstats
path: root/Software/Visual_Studio/Tango.Synchronization/Local
diff options
context:
space:
mode:
authorRoy Ben-Shabat <Roy@Twine-s.com>2017-11-27 21:51:38 +0200
committerRoy Ben-Shabat <Roy@Twine-s.com>2017-11-27 21:51:38 +0200
commitdf2f9b27b12356d6e92c64a72049f0fc4e6b86c0 (patch)
tree049ced63087ef30986cc998f8546071e5894e37e /Software/Visual_Studio/Tango.Synchronization/Local
parent7060dc80c707fc0441ff69fe4f899107cb3f6fc1 (diff)
downloadTango-df2f9b27b12356d6e92c64a72049f0fc4e6b86c0.tar.gz
Tango-df2f9b27b12356d6e92c64a72049f0fc4e6b86c0.zip
Started Remote <=> Local Synchronization... /;
Diffstat (limited to 'Software/Visual_Studio/Tango.Synchronization/Local')
-rw-r--r--Software/Visual_Studio/Tango.Synchronization/Local/Constants.cs35
-rw-r--r--Software/Visual_Studio/Tango.Synchronization/Local/ExtensionMethods.cs88
-rw-r--r--Software/Visual_Studio/Tango.Synchronization/Local/ILocalDataBase.cs93
-rw-r--r--Software/Visual_Studio/Tango.Synchronization/Local/LocalDBComparer.cs200
-rw-r--r--Software/Visual_Studio/Tango.Synchronization/Local/SqliteDataBase.cs434
5 files changed, 850 insertions, 0 deletions
diff --git a/Software/Visual_Studio/Tango.Synchronization/Local/Constants.cs b/Software/Visual_Studio/Tango.Synchronization/Local/Constants.cs
new file mode 100644
index 000000000..ed8084301
--- /dev/null
+++ b/Software/Visual_Studio/Tango.Synchronization/Local/Constants.cs
@@ -0,0 +1,35 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Tango.Synchronization.Local
+{
+ internal static class Constants
+ {
+ //SQLite Internal Constants
+ public const String SEQUENCE_TABLE_NAME = "sqlite_sequence";
+ public const String TABLE_INFO = "table_info";
+ public const String COLUMN_TYPE = "type";
+ public const String IS_NOT_NULL = "notnull";
+ public const String DEFAULT_VALUE = "dflt_value";
+
+ //Must Column Names.
+
+ /// <summary>
+ /// The table auto incremented id column name.
+ /// </summary>
+ public const String ID = "ID";
+
+ /// <summary>
+ /// The table unique identifier column name used for synchronization.
+ /// </summary>
+ public const String GUID = "GUID";
+
+ /// <summary>
+ /// The table last updated column name used for synchronization.
+ /// </summary>
+ public const String LAST_UPDATED = "LAST_UPDATED";
+ }
+}
diff --git a/Software/Visual_Studio/Tango.Synchronization/Local/ExtensionMethods.cs b/Software/Visual_Studio/Tango.Synchronization/Local/ExtensionMethods.cs
new file mode 100644
index 000000000..8746f9146
--- /dev/null
+++ b/Software/Visual_Studio/Tango.Synchronization/Local/ExtensionMethods.cs
@@ -0,0 +1,88 @@
+using Tango.Synchronization;
+using System;
+using System.Collections.Generic;
+using System.Data;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Tango.Synchronization.Local
+{
+ internal static class SQLExtensions
+ {
+ /// <summary>
+ /// Gets the type of the SQL column.
+ /// </summary>
+ /// <param name="column">The column.</param>
+ /// <returns></returns>
+ public static String GetSQLType(this DataColumn column)
+ {
+ return column.ExtendedProperties[Constants.COLUMN_TYPE].ToString();
+ }
+
+ /// <summary>
+ /// Gets the information table.
+ /// </summary>
+ /// <param name="table">The table.</param>
+ /// <returns></returns>
+ public static DataTable GetInfoTable(this DataTable table)
+ {
+ return table.ExtendedProperties[Constants.TABLE_INFO] as DataTable;
+ }
+
+ /// <summary>
+ /// Determines whether column is marked NOT NULL.
+ /// </summary>
+ /// <param name="column">The column.</param>
+ public static bool IsNotNull(this DataColumn column)
+ {
+ int value = int.Parse(column.ExtendedProperties[Constants.IS_NOT_NULL].ToString());
+ return value == 0 ? false : true;
+ }
+
+ /// <summary>
+ /// Gets the column default value.
+ /// </summary>
+ /// <param name="column">The column.</param>
+ /// <returns></returns>
+ public static String GetDefaultValue(this DataColumn column)
+ {
+ String def = column.ExtendedProperties[Constants.DEFAULT_VALUE].ToString();
+
+ double num = -1;
+
+ if (double.TryParse(def, out num))
+ {
+ return num.ToString();
+ }
+ else
+ {
+ if (def.Contains("randomblob") || def.Contains("localtime"))
+ {
+ def = def.Insert(0, "(");
+ def = def.Insert(def.Length - 1, ")");
+ }
+ return def;
+ }
+ }
+
+ /// <summary>
+ /// Determines whether column definition has default value.
+ /// </summary>
+ /// <param name="column">The column.</param>
+ public static bool HasDefaultValue(this DataColumn column)
+ {
+ return !String.IsNullOrWhiteSpace(column.ExtendedProperties[Constants.DEFAULT_VALUE].ToString());
+ }
+
+ /// <summary>
+ /// Converts the DateTime instance to SQLite DateTime string.
+ /// </summary>
+ /// <param name="date">The date.</param>
+ /// <returns></returns>
+ public static String ToSQLiteDateString(this DateTime date)
+ {
+ return date.ToString("yyyy-MM-dd HH:mm:ss.fff");
+ }
+ }
+} \ No newline at end of file
diff --git a/Software/Visual_Studio/Tango.Synchronization/Local/ILocalDataBase.cs b/Software/Visual_Studio/Tango.Synchronization/Local/ILocalDataBase.cs
new file mode 100644
index 000000000..7e53aeb1c
--- /dev/null
+++ b/Software/Visual_Studio/Tango.Synchronization/Local/ILocalDataBase.cs
@@ -0,0 +1,93 @@
+using System;
+using System.Collections.Generic;
+using System.Data;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Tango.Synchronization.Local
+{
+ /// <summary>
+ /// Represents an SQL data base adapter used by <see cref="LocalDBComparer"/>.
+ /// </summary>
+ /// <seealso cref="System.IDisposable" />
+ public interface ILocalDataBase : IDisposable
+ {
+ /// <summary>
+ /// Gets the database source URL/File.
+ /// </summary>
+ String Source { get; }
+
+ /// <summary>
+ /// Gets the database tables collection.
+ /// </summary>
+ List<DataTable> Tables { get; }
+
+ /// <summary>
+ /// Loads the tables (Must be done before any synchronization).
+ /// </summary>
+ void LoadTables();
+
+ /// <summary>
+ /// Clones a table from another database into this database.
+ /// </summary>
+ /// <param name="otherDB">The other database.</param>
+ /// <param name="otherTable">The other table.</param>
+ /// <returns></returns>
+ DataTable CloneTableFrom(ILocalDataBase otherDB, DataTable otherTable);
+
+ /// <summary>
+ /// Gets the SQL command for <see cref="CloneTableFrom(ILocalDataBase, DataTable)"/>.
+ /// </summary>
+ /// <param name="otherDB">The other database.</param>
+ /// <param name="otherTable">The other table.</param>
+ /// <returns></returns>
+ String GetCloneTableFromCommand(ILocalDataBase otherDB, DataTable otherTable);
+
+ /// <summary>
+ /// Adds the specified column to the specified table.
+ /// </summary>
+ /// <param name="table">The table.</param>
+ /// <param name="column">The column.</param>
+ /// <returns></returns>
+ DataColumn AddColumn(DataTable table, DataColumn column);
+
+ /// <summary>
+ /// Gets the SQL command for <see cref="AddColumn(DataTable, DataColumn)"/>.
+ /// </summary>
+ /// <param name="table">The table.</param>
+ /// <param name="column">The column.</param>
+ /// <returns></returns>
+ String GetAddColumnCommand(DataTable table, DataColumn column);
+
+ /// <summary>
+ /// Adds the specified row to the specified table.
+ /// </summary>
+ /// <param name="table">The table.</param>
+ /// <param name="row">The row.</param>
+ void AddRow(DataTable table, DataRow row);
+
+ /// <summary>
+ /// Gets the SQL command for <see cref="AddRow(DataTable, DataRow)"/>.
+ /// </summary>
+ /// <param name="table">The table.</param>
+ /// <param name="row">The row.</param>
+ /// <returns></returns>
+ String GetAddRowCommand(DataTable table, DataRow row);
+
+ /// <summary>
+ /// Updates the matching row by the row GUID.
+ /// </summary>
+ /// <param name="table">The table.</param>
+ /// <param name="row">The row.</param>
+ void UpdateRow(DataTable table, DataRow row);
+
+ /// <summary>
+ /// Gets the SQL command for <see cref="UpdateRow(DataTable, DataRow)"/>.
+ /// </summary>
+ /// <param name="table">The table.</param>
+ /// <param name="row">The row.</param>
+ /// <returns></returns>
+ String GetUpdateRowCommand(DataTable table, DataRow row);
+ }
+}
diff --git a/Software/Visual_Studio/Tango.Synchronization/Local/LocalDBComparer.cs b/Software/Visual_Studio/Tango.Synchronization/Local/LocalDBComparer.cs
new file mode 100644
index 000000000..63f2ecfaa
--- /dev/null
+++ b/Software/Visual_Studio/Tango.Synchronization/Local/LocalDBComparer.cs
@@ -0,0 +1,200 @@
+using System;
+using System.Collections.Generic;
+using System.Data;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Tango.Logging;
+
+namespace Tango.Synchronization.Local
+{
+ /// <summary>
+ /// Represents an <see cref="ILocalDataBase"/> synchronization engine.
+ /// </summary>
+ /// <seealso cref="System.IDisposable" />
+ public class LocalDBComparer : IDisposable
+ {
+ /// <summary>
+ /// Gets the master SQL.
+ /// </summary>
+ public ILocalDataBase MasterSQL { get; private set; }
+
+ /// <summary>
+ /// Gets the slave SQL.
+ /// </summary>
+ public ILocalDataBase SlaveSQL { get; private set; }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="LocalDBComparer"/> class.
+ /// </summary>
+ /// <param name="masterSQL">The master SQL.</param>
+ /// <param name="slaveSQL">The slave SQL.</param>
+ public LocalDBComparer(ILocalDataBase masterSQL, ILocalDataBase slaveSQL)
+ {
+ MasterSQL = masterSQL;
+ SlaveSQL = slaveSQL;
+ }
+
+ /// <summary>
+ /// Compares the master and slave SQL data base and returns a collection of <see cref="Diff"/>.
+ /// </summary>
+ /// <returns></returns>
+ public List<Diff> Compare()
+ {
+ LogManager.Log("Comparing databases " + Path.GetFileName(MasterSQL.Source) + " <=> " + Path.GetFileName(SlaveSQL.Source));
+
+ LogManager.Log("Loading master tables...");
+ MasterSQL.LoadTables();
+
+ LogManager.Log("Loading slave tables...");
+ SlaveSQL.LoadTables();
+
+ List<Diff> diffs = new List<Diff>();
+
+ foreach (var masterTable in MasterSQL.Tables)
+ {
+ LogManager.Log("Comparing table " + masterTable.TableName);
+ LogManager.Log("Searching table " + masterTable.TableName + " on slave...");
+
+ var slaveTable = SlaveSQL.Tables.SingleOrDefault(x => x.TableName == masterTable.TableName); //Get matching slave table on slave db.
+
+ if (slaveTable == null) //Table not found on slave.
+ {
+ LogManager.Log("Table not found on slave, adding difference.");
+ //Clone table from slave db to master db including records!
+ diffs.Add(new Diff(DiffAction.AddTableToSlave, String.Format("Add table {0} to slave", masterTable.TableName), () => SlaveSQL.CloneTableFrom(MasterSQL, masterTable), SlaveSQL.GetCloneTableFromCommand(MasterSQL, masterTable)));
+ continue;
+ }
+
+ LogManager.Log("Table found, comparing columns...");
+
+ foreach (DataColumn masterColumn in masterTable.Columns)
+ {
+ LogManager.Log("Searching for column " + masterColumn.ColumnName + " on slave...");
+
+ var slaveColumn = slaveTable.Columns[masterColumn.ColumnName]; //Get matching slave column on slave table.
+
+ if (slaveColumn == null) //Slave column not found.
+ {
+ LogManager.Log("Column not found on slave, adding difference.");
+ //Add column to slave table.
+ diffs.Add(new Diff(DiffAction.AddColumnToSlave, String.Format("Add column {0} to slave table", masterColumn.ColumnName), () => SlaveSQL.AddColumn(masterTable, masterColumn), SlaveSQL.GetAddColumnCommand(masterTable, masterColumn)));
+ }
+
+ LogManager.Log("Column found.");
+ }
+
+ List<DataRow> addToSlave = new List<DataRow>();
+ List<DataRow> updateSlave = new List<DataRow>();
+ List<DataRow> addToMaster = new List<DataRow>();
+ List<DataRow> updateMaster = new List<DataRow>();
+
+ LogManager.Log("Comparing rows...");
+
+ int count = 0;
+
+ foreach (DataRow masterRow in masterTable.Rows)
+ {
+ LogManager.Log("Comparing row " + count++);
+
+ String guid = masterRow.Field<String>(Constants.GUID);
+
+ LogManager.Log("Searching for row with GUID " + guid + " on slave table...");
+
+ //Get Matching slave row.
+ DataRow slaveRow = slaveTable.AsEnumerable().SingleOrDefault(x => x.Field<String>(Constants.GUID) == guid);
+
+ if (slaveRow != null)
+ {
+ LogManager.Log("Slave row found, comparing dates...");
+
+ DateTime masterDate = masterRow.Field<DateTime>(Constants.LAST_UPDATED);
+ DateTime slaveDate = slaveRow.Field<DateTime>(Constants.LAST_UPDATED);
+
+ if (masterDate > slaveDate)
+ {
+ LogManager.Log("Master => Slave Update " + masterDate.ToSQLiteDateString() + ", adding difference.");
+ updateSlave.Add(masterRow);
+ }
+ else if (slaveDate > masterDate)
+ {
+ LogManager.Log("Master <= Slave Update " + masterDate.ToSQLiteDateString() + ", adding difference.");
+ updateMaster.Add(slaveRow);
+ }
+ else
+ {
+ LogManager.Log("Master <=> Slave No Update.");
+ }
+ }
+ else
+ {
+ LogManager.Log("Slave row not found, adding difference.");
+ addToSlave.Add(masterRow);
+ }
+ }
+
+ LogManager.Log("Done comparing rows...");
+
+ LogManager.Log("Searching for missing rows on master...");
+
+ foreach (DataRow slaveRow in slaveTable.Rows)
+ {
+ String guid = slaveRow.Field<String>(Constants.GUID);
+
+ LogManager.Log("Searching for row with GUID " + guid + " on master table...");
+
+ //Get Matching slave row.
+ DataRow masterRow = masterTable.AsEnumerable().SingleOrDefault(x => x.Field<String>(Constants.GUID) == guid);
+
+ if (masterRow == null)
+ {
+ LogManager.Log("Master row not found, adding difference.");
+ addToMaster.Add(slaveRow);
+ }
+ else
+ {
+ LogManager.Log("Master row found.");
+ }
+ }
+
+ LogManager.Log("Done searching for missing rows on master...");
+
+ foreach (var row in addToSlave)
+ {
+ diffs.Add(new Diff(DiffAction.AddRowToSlave, String.Format("Add row to slave table {0}", slaveTable.TableName), () => SlaveSQL.AddRow(slaveTable, row), SlaveSQL.GetAddRowCommand(slaveTable, row)));
+ }
+
+ foreach (var row in addToMaster)
+ {
+ diffs.Add(new Diff(DiffAction.AddRowToMaster, String.Format("Add row to master table {0}", masterTable.TableName), () => MasterSQL.AddRow(masterTable, row), MasterSQL.GetAddRowCommand(masterTable, row)));
+ }
+
+ foreach (var row in updateSlave)
+ {
+ diffs.Add(new Diff(DiffAction.UpdateRowInSlave, String.Format("Update row in slave table {0}", slaveTable.TableName), () => SlaveSQL.UpdateRow(slaveTable, row), SlaveSQL.GetUpdateRowCommand(slaveTable, row)));
+ }
+
+ foreach (var row in updateMaster)
+ {
+ diffs.Add(new Diff(DiffAction.UpdateRowInMaster, String.Format("Update row in master table {0}", masterTable.TableName), () => MasterSQL.UpdateRow(masterTable, row), MasterSQL.GetUpdateRowCommand(masterTable, row)));
+ }
+
+ LogManager.Log("Done comparing table " + masterTable.TableName);
+ }
+
+ LogManager.Log("Databases comparison completed.");
+
+ return diffs;
+ }
+
+ /// <summary>
+ /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
+ /// </summary>
+ public void Dispose()
+ {
+ MasterSQL.Dispose();
+ SlaveSQL.Dispose();
+ }
+ }
+}
diff --git a/Software/Visual_Studio/Tango.Synchronization/Local/SqliteDataBase.cs b/Software/Visual_Studio/Tango.Synchronization/Local/SqliteDataBase.cs
new file mode 100644
index 000000000..35e54c66a
--- /dev/null
+++ b/Software/Visual_Studio/Tango.Synchronization/Local/SqliteDataBase.cs
@@ -0,0 +1,434 @@
+using Tango.Synchronization;
+using System;
+using System.Collections.Generic;
+using System.Data;
+using System.Data.SQLite;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Tango.Synchronization.Local
+{
+ /// <summary>
+ /// Represents an SQLite database adapter used for synchronization by <see cref="LocalDBComparer"/>.
+ /// </summary>
+ /// <seealso cref="Tango.Synchronization.ISqlDataBase" />
+ public class SQLiteDataBase : ILocalDataBase
+ {
+ private SQLiteConnection _connection;
+
+ /// <summary>
+ /// Gets the database source URL/File.
+ /// </summary>
+ public String Source { get; private set; }
+
+ /// <summary>
+ /// Gets the database tables collection.
+ /// </summary>
+ public List<DataTable> Tables { get; private set; }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="SQLiteDataBase"/> class.
+ /// </summary>
+ /// <param name="filePath">The file path.</param>
+ public SQLiteDataBase(String filePath)
+ {
+ Source = filePath;
+
+ _connection = new SQLiteConnection(String.Format("Data Source={0};Version=3;New={1};Compress=FALSE;",
+ filePath,
+ (File.Exists(filePath) ? "False" : "True")
+ ));
+
+ _connection.Open();
+ }
+
+ /// <summary>
+ /// Loads the tables (Must be done before any synchronization).
+ /// </summary>
+ public void LoadTables()
+ {
+ Tables = new List<DataTable>();
+
+ var command = _connection.CreateCommand();
+ command.CommandText = "SELECT name FROM sqlite_master WHERE type = 'table'";
+ var reader = command.ExecuteReader();
+
+ while (reader.Read())
+ {
+ String name = reader.GetString(0);
+
+ if (name != Constants.SEQUENCE_TABLE_NAME)
+ {
+ Debug.WriteLine(name);
+ Tables.Add(LoadTable(name));
+ }
+ }
+ }
+
+ /// <summary>
+ /// Loads the table.
+ /// </summary>
+ /// <param name="name">The name.</param>
+ /// <returns></returns>
+ private DataTable LoadTable(String name)
+ {
+ var tableCommand = _connection.CreateCommand();
+ tableCommand.CommandText = "SELECT * FROM " + name;
+
+ DataTable table = new DataTable(name);
+ table.Load(tableCommand.ExecuteReader());
+
+ var infoCommand = _connection.CreateCommand();
+ infoCommand.CommandText = String.Format("PRAGMA table_info({0});", name);
+ DataTable infoTable = new DataTable(name);
+ infoTable.Load(infoCommand.ExecuteReader());
+
+ table.ExtendedProperties.Add(Constants.TABLE_INFO, infoTable);
+
+ for (int i = 0; i < table.Columns.Count; i++)
+ {
+ DataColumn column = table.Columns[i];
+ column.ExtendedProperties.Add(Constants.COLUMN_TYPE, infoTable.Rows[i][Constants.COLUMN_TYPE]);
+ column.ExtendedProperties.Add(Constants.IS_NOT_NULL, infoTable.Rows[i][Constants.IS_NOT_NULL]);
+ column.ExtendedProperties.Add(Constants.DEFAULT_VALUE, infoTable.Rows[i][Constants.DEFAULT_VALUE]);
+ }
+
+ return table;
+ }
+
+ /// <summary>
+ /// Clones a table from another database into this database.
+ /// </summary>
+ /// <param name="otherDB">The other database.</param>
+ /// <param name="otherTable">The other table.</param>
+ /// <returns></returns>
+ public DataTable CloneTableFrom(ILocalDataBase otherDB, DataTable otherTable)
+ {
+ String cmd = String.Format("CREATE TABLE {0} (", otherTable.TableName);
+
+ foreach (DataColumn column in otherTable.Columns)
+ {
+ cmd += String.Format("{0} {1} {2} {3} {4} {5}",
+ column.ColumnName,
+ column.GetSQLType(),
+ otherTable.PrimaryKey.Contains(column) ? "PRIMARY KEY" : null,
+ column.Unique ? "UNIQUE" : null,
+ column.IsNotNull() ? "NOT NULL" : null,
+ column.HasDefaultValue() ? ("DEFAULT " + column.GetDefaultValue()) : null)
+
+ + ", " + Environment.NewLine;
+ }
+
+ cmd += ");";
+
+ cmd = cmd.Remove(cmd.LastIndexOf(","), 1);
+
+ var createCommand = _connection.CreateCommand();
+ createCommand.CommandText = cmd;
+ createCommand.ExecuteNonQuery();
+
+ var attacheCommand = _connection.CreateCommand();
+ attacheCommand.CommandText = String.Format("ATTACH DATABASE '{0}' AS other;", otherDB.Source);
+ attacheCommand.ExecuteNonQuery();
+
+ var copyCommand = _connection.CreateCommand();
+ copyCommand.CommandText = String.Format("INSERT INTO main.{0} SELECT * FROM {1}.{0};", otherTable.TableName, "other");
+ copyCommand.ExecuteNonQuery();
+
+ var detacheCommand = _connection.CreateCommand();
+ detacheCommand.CommandText = "DETACH other;";
+ detacheCommand.ExecuteNonQuery();
+
+ var table = LoadTable(otherTable.TableName);
+ Tables.Add(table);
+ return table;
+ }
+
+ /// <summary>
+ /// Gets the SQL command for <see cref="CloneTableFrom(ILocalDataBase, DataTable)" />.
+ /// </summary>
+ /// <param name="otherDB">The other database.</param>
+ /// <param name="otherTable">The other table.</param>
+ /// <returns></returns>
+ public String GetCloneTableFromCommand(ILocalDataBase otherDB, DataTable otherTable)
+ {
+ String cmd = String.Format("CREATE TABLE {0} (", otherTable.TableName);
+
+ foreach (DataColumn column in otherTable.Columns)
+ {
+ cmd += String.Format("{0} {1} {2} {3} {4} {5}",
+ column.ColumnName,
+ column.GetSQLType(),
+ otherTable.PrimaryKey.Contains(column) ? "PRIMARY KEY" : null,
+ column.Unique ? "UNIQUE" : null,
+ column.IsNotNull() ? "NOT NULL" : null,
+ column.HasDefaultValue() ? ("DEFAULT " + column.GetDefaultValue()) : null)
+
+ + ", " + Environment.NewLine;
+ }
+
+ cmd += ");";
+
+ cmd = cmd.Remove(cmd.LastIndexOf(","), 1);
+
+ cmd += Environment.NewLine;
+
+ cmd += String.Format("ATTACH DATABASE '{0}' AS other;", otherDB.Source);
+
+ cmd += Environment.NewLine;
+
+ cmd += String.Format("INSERT INTO main.{0} SELECT * FROM {1}.{0};", otherTable.TableName, "other");
+
+ cmd += Environment.NewLine;
+
+ cmd += "DETACH other;";
+
+ return cmd;
+ }
+
+ /// <summary>
+ /// Adds the specified column to the specified table.
+ /// </summary>
+ /// <param name="table">The table.</param>
+ /// <param name="column">The column.</param>
+ /// <returns></returns>
+ public DataColumn AddColumn(DataTable table, DataColumn column)
+ {
+ var insertCommand = _connection.CreateCommand();
+ insertCommand.CommandText = String.Format("ALTER TABLE {0} ADD COLUMN {1} {2} {3} {4} {5} {6}",
+ table.TableName,
+ column.ColumnName,
+ column.GetSQLType(),
+ table.PrimaryKey.Contains(column) ? "PRIMARY KEY" : null,
+ column.Unique ? "UNIQUE" : null,
+ column.IsNotNull() ? "NOT NULL" : null,
+ column.HasDefaultValue() ? ("DEFAULT " + column.GetDefaultValue()) : null);
+
+
+ insertCommand.ExecuteNonQuery();
+ var t = LoadTable(table.TableName);
+ var index = Tables.IndexOf(Tables.Single(x => x.TableName == table.TableName));
+ Tables[index] = t;
+ return t.Columns[column.ColumnName];
+ }
+
+ /// <summary>
+ /// Gets the SQL command for <see cref="AddColumn(DataTable, DataColumn)" />.
+ /// </summary>
+ /// <param name="table">The table.</param>
+ /// <param name="column">The column.</param>
+ /// <returns></returns>
+ public String GetAddColumnCommand(DataTable table, DataColumn column)
+ {
+ String cmd = String.Format("ALTER TABLE {0} ADD COLUMN {1} {2} {3} {4} {5} {6}",
+ table.TableName,
+ column.ColumnName,
+ column.GetSQLType(),
+ table.PrimaryKey.Contains(column) ? "PRIMARY KEY" : null,
+ column.Unique ? "UNIQUE" : null,
+ column.IsNotNull() ? "NOT NULL" : null,
+ column.HasDefaultValue() ? ("DEFAULT " + column.GetDefaultValue()) : null);
+
+ return cmd;
+ }
+
+ /// <summary>
+ /// Adds the specified row to the specified table.
+ /// </summary>
+ /// <param name="table">The table.</param>
+ /// <param name="row">The row.</param>
+ public void AddRow(DataTable table, DataRow row)
+ {
+ var insertCommand = _connection.CreateCommand();
+
+ List<String> values = new List<string>();
+
+ for (int i = 0; i < row.ItemArray.Length; i++)
+ {
+ String value = row.ItemArray[i].ToString();
+
+ double num = 0;
+
+ if (row.ItemArray[i].GetType() == typeof(DateTime))
+ {
+ value = ((DateTime)row.ItemArray[i]).ToSQLiteDateString();
+ }
+
+ if (!double.TryParse(value, out num))
+ {
+ value = "'" + value + "'";
+ }
+
+ values.Add(value);
+ }
+
+ insertCommand.CommandText = String.Format("INSERT INTO {0} VALUES({1});", table.TableName, String.Join(",", values));
+ insertCommand.ExecuteNonQuery();
+
+ var desRow = table.NewRow();
+ desRow.ItemArray = row.ItemArray.Clone() as object[];
+ }
+
+ /// <summary>
+ /// Gets the SQL command for <see cref="AddRow(DataTable, DataRow)" />.
+ /// </summary>
+ /// <param name="table">The table.</param>
+ /// <param name="row">The row.</param>
+ /// <returns></returns>
+ public String GetAddRowCommand(DataTable table, DataRow row)
+ {
+ List<String> values = new List<string>();
+
+ for (int i = 0; i < row.ItemArray.Length; i++)
+ {
+ String value = row.ItemArray[i].ToString();
+
+ double num = 0;
+
+ if (row.ItemArray[i].GetType() == typeof(DateTime))
+ {
+ value = ((DateTime)row.ItemArray[i]).ToSQLiteDateString();
+ }
+
+ if (!double.TryParse(value, out num))
+ {
+ value = "'" + value + "'";
+ }
+
+ values.Add(value);
+ }
+
+ String cmd = String.Format("INSERT INTO {0} VALUES({1});", table.TableName, String.Join("," + Environment.NewLine, values));
+ return cmd;
+ }
+
+ /// <summary>
+ /// Updates the matching row by the row GUID.
+ /// </summary>
+ /// <param name="table">The table.</param>
+ /// <param name="row">The row.</param>
+ public void UpdateRow(DataTable table, DataRow row)
+ {
+ var updateCommand = _connection.CreateCommand();
+
+ List<String> values = new List<string>();
+
+ for (int i = 0; i < row.ItemArray.Length; i++)
+ {
+ String value = row.ItemArray[i].ToString();
+
+ double num = 0;
+
+ if (row.ItemArray[i].GetType() == typeof(DateTime))
+ {
+ value = ((DateTime)row.ItemArray[i]).ToSQLiteDateString();
+ }
+
+ if (!double.TryParse(value, out num))
+ {
+ value = "'" + value + "'";
+ }
+
+ values.Add(value);
+ }
+
+ String cmd = String.Format("UPDATE {0} SET ", table.TableName);
+
+ String guid = String.Empty;
+
+ for (int columnIndex = 0; columnIndex < table.Columns.Count; columnIndex++)
+ {
+ DataColumn column = table.Columns[columnIndex];
+ if (column.ColumnName == Constants.ID)
+ {
+ continue;
+ }
+ else if (column.ColumnName == Constants.GUID)
+ {
+ guid = values[columnIndex];
+ continue;
+ }
+
+ cmd += String.Format("{0} = {1},{2}", column.ColumnName, values[columnIndex], Environment.NewLine);
+ }
+
+ cmd = cmd.Remove(cmd.LastIndexOf(","), 1);
+
+ cmd += "WHERE" + Environment.NewLine;
+ cmd += String.Format("{0} = {1};", Constants.GUID, guid);
+
+ updateCommand.CommandText = cmd;
+ updateCommand.ExecuteNonQuery();
+ var desRow = table.NewRow();
+ desRow.ItemArray = row.ItemArray.Clone() as object[];
+ }
+
+ /// <summary>
+ /// Gets the SQL command for <see cref="UpdateRow(DataTable, DataRow)" />.
+ /// </summary>
+ /// <param name="table">The table.</param>
+ /// <param name="row">The row.</param>
+ /// <returns></returns>
+ public String GetUpdateRowCommand(DataTable table, DataRow row)
+ {
+ List<String> values = new List<string>();
+
+ for (int i = 0; i < row.ItemArray.Length; i++)
+ {
+ String value = row.ItemArray[i].ToString();
+
+ double num = 0;
+
+ if (row.ItemArray[i].GetType() == typeof(DateTime))
+ {
+ value = ((DateTime)row.ItemArray[i]).ToSQLiteDateString();
+ }
+
+ if (!double.TryParse(value, out num))
+ {
+ value = "'" + value + "'";
+ }
+
+ values.Add(value);
+ }
+
+ String cmd = String.Format("UPDATE {0} SET ", table.TableName);
+
+ String guid = String.Empty;
+
+ for (int columnIndex = 0; columnIndex < table.Columns.Count; columnIndex++)
+ {
+ DataColumn column = table.Columns[columnIndex];
+ if (column.ColumnName == Constants.ID)
+ {
+ continue;
+ }
+ else if (column.ColumnName == Constants.GUID)
+ {
+ guid = values[columnIndex];
+ continue;
+ }
+
+ cmd += String.Format("{0} = {1},{2}", column.ColumnName, values[columnIndex], Environment.NewLine);
+ }
+
+ cmd = cmd.Remove(cmd.LastIndexOf(","), 1);
+
+ cmd += "WHERE" + Environment.NewLine;
+ cmd += String.Format("{0} = {1};", Constants.GUID, guid);
+
+ return cmd;
+ }
+
+ /// <summary>
+ /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
+ /// </summary>
+ public void Dispose()
+ {
+ _connection.Close();
+ }
+ }
+}