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;
using System.Threading;
namespace Tango.Synchronization.Local
{
///
/// Represents an SQLite database adapter used for synchronization by .
///
///
public class SQLiteDataBase : ILocalDataBase
{
private SQLiteConnection _connection;
///
/// Gets the database source URL/File.
///
public String Source { get; private set; }
///
/// Gets the database tables collection.
///
public List Tables { get; private set; }
///
/// Initializes a new instance of the class.
///
/// The file path.
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();
}
///
/// Loads the tables (Must be done before any synchronization).
///
public void LoadTables()
{
Tables = new List();
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));
}
}
}
///
/// Loads the table.
///
/// The name.
///
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;
}
///
/// Clears the data base.
///
public void ClearDataBase()
{
foreach (var table in Tables)
{
var dropCommand = _connection.CreateCommand();
dropCommand.CommandText = String.Format("DELETE FROM {0};", table.TableName);
dropCommand.ExecuteNonQuery();
}
}
///
/// Clones a table from another database into this database.
///
/// The other database.
/// The other table.
///
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;
}
///
/// Replaces the table data with the data from another table.
///
/// The other table.
public void ReplaceTableData(ILocalDataBase otherDB, DataTable otherTable)
{
var dropCommand = _connection.CreateCommand();
dropCommand.CommandText = String.Format("DELETE FROM {0};", otherTable.TableName);
dropCommand.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();
}
///
/// Gets the SQL command for .
///
///
/// The other table.
///
public string GetReplaceTableDataCommand(ILocalDataBase otherDB, DataTable otherTable)
{
String cmd = String.Format("DELETE FROM {0};", otherTable.TableName) + Environment.NewLine;
cmd += String.Format("ATTACH DATABASE '{0}' AS other;", otherDB.Source) + Environment.NewLine;
cmd += String.Format("INSERT INTO main.{0} SELECT * FROM {1}.{0};", otherTable.TableName, "other") + Environment.NewLine;
cmd += "DETACH other;" + Environment.NewLine;
return cmd;
}
///
/// Gets the SQL command for .
///
/// The other database.
/// The other table.
///
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;
}
///
/// Adds the specified column to the specified table.
///
/// The table.
/// The column.
///
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}",
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];
}
///
/// Gets the SQL command for .
///
/// The table.
/// The column.
///
public String GetAddColumnCommand(DataTable table, DataColumn column)
{
String cmd = String.Format("ALTER TABLE {0} ADD COLUMN {1} {2} {3} {4} {5}",
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;
}
///
/// Adds the specified row to the specified table.
///
/// The table.
/// The row.
public void AddRow(DataTable table, DataRow row)
{
var insertCommand = _connection.CreateCommand();
List values = new List();
for (int i = 0; i < row.ItemArray.Length; i++)
{
String value = row.ItemArray[i].ToString();
double num = 0;
if (row.ItemArray[i].GetType() == typeof(bool))
{
value = ((bool)row.ItemArray[i]) ? 1.ToString() : 0.ToString();
}
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[];
}
///
/// Gets the SQL command for .
///
/// The table.
/// The row.
///
public String GetAddRowCommand(DataTable table, DataRow row)
{
List values = new List();
for (int i = 0; i < row.ItemArray.Length; i++)
{
String value = row.ItemArray[i].ToString();
double num = 0;
if (row.ItemArray[i].GetType() == typeof(bool))
{
value = ((bool)row.ItemArray[i]) ? 1.ToString() : 0.ToString();
}
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;
}
///
/// Updates the matching row by the row GUID.
///
/// The table.
/// The row.
public void UpdateRow(DataTable table, DataRow row)
{
var updateCommand = _connection.CreateCommand();
List values = new List();
for (int i = 0; i < row.ItemArray.Length; i++)
{
String value = row.ItemArray[i].ToString();
double num = 0;
if (row.ItemArray[i].GetType() == typeof(bool))
{
value = ((bool)row.ItemArray[i]) ? 1.ToString() : 0.ToString();
}
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[];
}
///
/// Gets the SQL command for .
///
/// The table.
/// The row.
///
public String GetUpdateRowCommand(DataTable table, DataRow row)
{
List values = new List();
for (int i = 0; i < row.ItemArray.Length; i++)
{
String value = row.ItemArray[i].ToString();
double num = 0;
if (row.ItemArray[i].GetType() == typeof(bool))
{
value = ((bool)row.ItemArray[i]) ? 1.ToString() : 0.ToString();
}
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;
}
///
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
///
public void Dispose()
{
_connection.Close();
_connection.Dispose();
GC.Collect();
DateTime startTime = DateTime.Now;
while (startTime.AddSeconds(2) > DateTime.Now)
{
try
{
using (FileStream stream = File.Open(Source, FileMode.Open, FileAccess.Read))
{
return;
}
}
catch (IOException)
{
GC.Collect();
Thread.Sleep(200);
}
}
}
}
}