aboutsummaryrefslogtreecommitdiffstats
path: root/Software/Visual_Studio/Tango.Synchronization/Local/SqliteDataBase.cs
blob: 8e2656a98c2fdb4423e29810537d6f9da0851d90 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
pre { line-height: 125%; }
td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
.highlight .hll { background-color: #ffffcc }
.highlight .c { color: #888888 } /* Comment */
.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */
.highlight .k { color: #008800; font-weight: bold } /* Keyword */
.highlight .ch { color: #888888 } /* Comment.Hashbang */
.highlight .cm { color: #888888 } /* Comment.Multiline */
.highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */
.highlight .cpf { color: #888888 } /* Comment.PreprocFile */
.highlight .c1 { color: #888888 } /* Comment.Single */
.highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */
.highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .ges { font-weight: bold; font-style: italic } /* Generic.EmphStrong */
.highlight .gr { color: #aa0000 } /* Generic.Error */
.highlight .gh { color: #333333 } /* Generic.Heading */
.highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
.highlight .go { color: #888888 } /* Generic.Output */
.highlight .gp { color: #555555 } /* Generic.Prompt */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #666666 } /* Generic.Subheading */
.highlight .gt { color: #aa0000 } /* Generic.Traceback */
.highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */
.highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */
.highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */
.highlight .kp { color: #008800 } /* Keyword.Pseudo */
.highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */
.highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */
.highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */
.highlight .na { color: #336699 } /* Name.Attribute */
.highlight .nb { color: #003388 } /* Name.Builtin */
.highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */
.highlight .no { color: #003366; font-weight: bold } /* Name.Constant */
.highlight .nd { color: #555555 } /* Name.Decorator */
.highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */
.highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */
.highlight .nl { color: #336699; font-style: italic } /* Name.Label */
.highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */
.highlight .py { color: #336699; font-weight: bold } /* Name.Property */
.highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */
.highlight .nv { color: #336699 } /* Name.Variable */
.highlight .ow { color: #008800 } /* Operator.Word */
.highlight .w { color: #bbbbbb } /* Text.Whitespace */
.highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */
.highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */
.highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */
.highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */
.highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */
.highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */
.highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */
.highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */
.highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */
.highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */
.highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */
.highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */
.highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */
.highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */
.highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */
.highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */
.highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */
.highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */
.highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */
.highlight .vc { color: #336699 } /* Name.Variable.Class */
.highlight .vg { color: #dd7700 } /* Name.Variable.Global */
.highlight .vi { color: #3333bb } /* Name.Variable.Instance */
.highlight .vm { color: #336699 } /* Name.Variable.Magic */
.highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */
// Generated by the protocol buffer compiler.  DO NOT EDIT!
// source: Event.proto
#pragma warning disable 1591, 0612, 3021
#region Designer generated code

using pb = global::Google.Protobuf;
using pbc = global::Google.Protobuf.Collections;
using pbr = global::Google.Protobuf.Reflection;
using scg = global::System.Collections.Generic;
namespace Tango.PMR.Diagnostics {

  /// <summary>Holder for reflection information generated from Event.proto</summary>
  public static partial class EventReflection {

    #region Descriptor
    /// <summary>File descriptor for Event.proto</summary>
    public static pbr::FileDescriptor Descriptor {
      get { return descriptor; }
    }
    private static pbr::FileDescriptor descriptor;

    static EventReflection() {
      byte[] descriptorData = global::System.Convert.FromBase64String(
          string.Concat(
            "CgtFdmVudC5wcm90bxIVVGFuZ28uUE1SLkRpYWdub3N0aWNzGg9FdmVudFR5",
            "cGUucHJvdG8iSAoFRXZlbnQSLgoEVHlwZRgBIAEoDjIgLlRhbmdvLlBNUi5E",
            "aWFnbm9zdGljcy5FdmVudFR5cGUSDwoHTWVzc2FnZRgCIAEoCUIhCh9jb20u",
            "dHdpbmUudGFuZ28ucG1yLmRpYWdub3N0aWNzYgZwcm90bzM="));
      descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData,
          new pbr::FileDescriptor[] { global::Tango.PMR.Diagnostics.EventTypeReflection.Descriptor, },
          new pbr::GeneratedClrTypeInfo(null, new pbr::GeneratedClrTypeInfo[]pre { line-height: 125%; }
td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
.highlight .hll { background-color: #ffffcc }
.highlight .c { color: #888888 } /* Comment */
.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */
.highlight .k { color: #008800; font-weight: bold } /* Keyword */
.highlight .ch { color: #888888 } /* Comment.Hashbang */
.highlight .cm { color: #888888 } /* Comment.Multiline */
.highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */
.highlight .cpf { color: #888888 } /* Comment.PreprocFile */
.highlight .c1 { color: #888888 } /* Comment.Single */
.highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */
.highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .ges { font-weight: bold; font-style: italic } /* Generic.EmphStrong */
.highlight .gr { color: #aa0000 } /* Generic.Error */
.highlight .gh { color: #333333 } /* Generic.Heading */
.highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
.highlight .go { color: #888888 } /* Generic.Output */
.highlight .gp { color: #555555 } /* Generic.Prompt */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #666666 } /* Generic.Subheading */
.highlight .gt { color: #aa0000 } /* Generic.Traceback */
.highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */
.highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */
.highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */
.highlight .kp { color: #008800 } /* Keyword.Pseudo */
.highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */
.highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */
.highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */
.highlight .na { color: #336699 } /* Name.Attribute */
.highlight .nb { color: #003388 } /* Name.Builtin */
.highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */
.highlight .no { color: #003366; font-weight: bold } /* Name.Constant */
.highlight .nd { color: #555555 } /* Name.Decorator */
.highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */
.highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */
.highlight .nl { color: #336699; font-style: italic } /* Name.Label */
.highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */
.highlight .py { color: #336699; font-weight: bold } /* Name.Property */
.highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */
.highlight .nv { color: #336699 } /* Name.Variable */
.highlight .ow { color: #008800 } /* Operator.Word */
.highlight .w { color: #bbbbbb } /* Text.Whitespace */
.highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */
.highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */
.highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */
.highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */
.highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */
.highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */
.highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */
.highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */
.highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */
.highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */
.highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */
.highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */
.highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */
.highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */
.highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */
.highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */
.highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */
.highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */
.highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */
.highlight .vc { color: #336699 } /* Name.Variable.Class */
.highlight .vg { color: #dd7700 } /* Name.Variable.Global */
.highlight .vi { color: #3333bb } /* Name.Variable.Instance */
.highlight .vm { color: #336699 } /* Name.Variable.Magic */
.highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */
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
{
    /// <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>
        /// Clears the data base.
        /// </summary>
        public void ClearDataBase()
        {
            foreach (var table in Tables)
            {
                var dropCommand = _connection.CreateCommand();
                dropCommand.CommandText = String.Format("DELETE FROM {0};", table.TableName);
                dropCommand.ExecuteNonQuery();
            }
        }

        /// <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>
        /// Replaces the table data with the data from another table.
        /// </summary>
        /// <param name="otherTable">The other table.</param>
        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();
        }

        /// <summary>
        /// Gets the SQL command for <see cref="ReplaceTableData(ILocalDataBase, DataTable)"/>.
        /// </summary>
        /// <param name="otherDB"></param>
        /// <param name="otherTable">The other table.</param>
        /// <returns></returns>
        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;
        }

        /// <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}",
                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}",
                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(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[];
        }

        /// <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(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;
        }

        /// <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(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[];
        }

        /// <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(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;
        }

        /// <summary>
        /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
        /// </summary>
        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);
                }
            }
        }
    }
}