Click here to Skip to main content
15,896,063 members
Articles / Desktop Programming / Windows Forms

SQLite Compare Utility

Rate me:
Please Sign up or sign in to vote.
4.89/5 (68 votes)
21 Feb 2015LGPL35 min read 285.4K   37.2K   131  
Utility for comparing two SQLite database files for both structure and data
using System;
using System.Text.RegularExpressions;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;
using SQLiteParser;

namespace SQLiteTurbo
{
    public partial class SchemaComparisonView : UserControl
    {
        #region Events
        /// <summary>
        /// Fired whenever the user changes selection in the grid.
        /// </summary>
        public event EventHandler SelectionChanged;
        #endregion

        #region Constructors
        public SchemaComparisonView()
        {
            InitializeComponent();
            _strikeout = new Font(this.Font, FontStyle.Strikeout);
        }
        #endregion

        #region Public Methods
        /// <summary>
        /// Used to set the comparison results into the view
        /// </summary>
        /// <param name="results">The results object</param>
        /// <param name="leftDb">The path to the left DB file</param>
        /// <param name="rightDb">The path to the right DB file</param>
        public void ShowComparisonResults(
            Dictionary<SchemaObject, List<SchemaComparisonItem>> results,
            string leftDb, string rightDb, Dictionary<SchemaObject, Dictionary<string, SQLiteDdlStatement>> leftSchema,
            Dictionary<SchemaObject, Dictionary<string, SQLiteDdlStatement>> rightSchema,
            bool dataWasCompared)
        {
            _leftdb = leftDb;
            _rightdb = rightDb;
            _leftSchema = leftSchema;
            _rightSchema = rightSchema;
            colLeft.HeaderText = leftDb;
            colRight.HeaderText = rightDb;
            lblTableDataIsDifferent.Visible = dataWasCompared;
            _results = results;
            UpdateView();
            ShowMatchingRows();
        }
        
        /// <summary>
        /// Returns TRUE if there is another difference in the grid after the selected row.
        /// </summary>
        public bool HasNextDiff()
        {
            if (_adding || grdSchemaDiffs.SelectedRows.Count == 0)
                return false;
            DataGridViewRow row = grdSchemaDiffs.SelectedRows[0];
            for (int index = row.Index + 1; index < grdSchemaDiffs.Rows.Count; index++)
            {
                row = grdSchemaDiffs.Rows[index];
                if (!row.Visible)
                    continue;
                SchemaComparisonItem item = (SchemaComparisonItem)row.Tag;
                if (item.Result != ComparisonResult.Same || (item.TableChanges != null && !item.TableChanges.SameTables))
                    return true;
            } // for

            return false;
        }

        /// <summary>
        /// Move selection to the next difference in the grid
        /// </summary>
        public void MoveToNextDiff()
        {
            if (_adding || grdSchemaDiffs.SelectedRows.Count == 0)
                return;
            DataGridViewRow row = grdSchemaDiffs.SelectedRows[0];
            for (int index = row.Index+1; index < grdSchemaDiffs.Rows.Count; index++)
            {
                row = grdSchemaDiffs.Rows[index];
                if (!row.Visible)
                    continue;
                SchemaComparisonItem item = (SchemaComparisonItem)row.Tag;
                if (item.Result != ComparisonResult.Same || (item.TableChanges != null && !item.TableChanges.SameTables))
                {
                    grdSchemaDiffs.ClearSelection();
                    row.Selected = true;
                    grdSchemaDiffs.FirstDisplayedScrollingRowIndex = row.Index;
                    return;
                }                
            } // for
        }

        /// <summary>
        /// Returns TRUE if there is a previous difference before the 
        /// selected row.
        /// </summary>
        public bool HasPreviousDiff()
        {
            if (_adding || grdSchemaDiffs.SelectedRows.Count == 0)
                return false;
            DataGridViewRow row = grdSchemaDiffs.SelectedRows[0];
            for (int index = row.Index - 1; index >= 0; index--)
            {
                row = grdSchemaDiffs.Rows[index];
                if (!row.Visible)
                    continue;
                SchemaComparisonItem item = (SchemaComparisonItem)row.Tag;
                if (item.Result != ComparisonResult.Same || (item.TableChanges != null && !item.TableChanges.SameTables))
                    return true;
            } // for            
            return false;
        }

        /// <summary>
        /// Move selection to the previous difference in the grid
        /// </summary>
        public void MoveToPreviousDiff()
        {
            if (_adding || grdSchemaDiffs.SelectedRows.Count == 0)
                return;
            DataGridViewRow row = grdSchemaDiffs.SelectedRows[0];
            for (int index = row.Index - 1; index >= 0; index--)
            {
                row = grdSchemaDiffs.Rows[index];
                if (!row.Visible)
                    continue;

                SchemaComparisonItem item = (SchemaComparisonItem)row.Tag;
                if (item.Result != ComparisonResult.Same || (item.TableChanges != null && !item.TableChanges.SameTables))
                {
                    grdSchemaDiffs.ClearSelection();
                    row.Selected = true;                    
                    grdSchemaDiffs.FirstDisplayedScrollingRowIndex = row.Index;
                    return;
                }
            } // for            
        }

        /// <summary>
        /// Returns TRUE if the user can copy the DB entity from the left database
        /// to the right database.
        /// </summary>
        public bool CanCopyFromLeftDB()
        {
            if (_adding || grdSchemaDiffs.SelectedRows.Count == 0)
                return false;
            DataGridViewRow row = grdSchemaDiffs.SelectedRows[0];
            if (!row.Visible)
                return false;

            SchemaComparisonItem item = (SchemaComparisonItem)row.Tag;
            if (item.LeftDdlStatement == null && item.RightDdlStatement == null)
                return false;

            return true;
        }

        /// <summary>
        /// Copies the DB entity stored in the left DB to the right DB
        /// </summary>
        public void CopyFromLeftDB()
        {
            if (_adding || grdSchemaDiffs.SelectedRows.Count == 0)
                return;
            DataGridViewRow row = grdSchemaDiffs.SelectedRows[0];
            SchemaComparisonItem item = (SchemaComparisonItem)row.Tag;

            if (item.Result == ComparisonResult.ExistsInLeftDB ||
                item.Result == ComparisonResult.DifferentSchema ||
                item.Result == ComparisonResult.DifferentData ||
                item.Result == ComparisonResult.Same)
            {
                DialogResult res = MessageBox.Show(this,
                    "Are you sure you want to copy " + Utils.GetItemObjectTypeName(item) +
                    " " + item.ObjectName + " " +
                    "from the left database to the right datbase?",
                    "Confirm copying",
                    MessageBoxButtons.YesNo,
                    MessageBoxIcon.Warning,
                    MessageBoxDefaultButton.Button2);
                if (res == DialogResult.No)
                    return;
            }
            else if (item.Result == ComparisonResult.ExistsInRightDB)
            {
                DialogResult res = MessageBox.Show(this,
                    "Are you sure you want to delete " + Utils.GetItemObjectTypeName(item) +
                    " " + item.ObjectName + " " +
                    "from the right database?", "Confirm deletion",
                    MessageBoxButtons.YesNo,
                    MessageBoxIcon.Warning,
                    MessageBoxDefaultButton.Button2);
                if (res == DialogResult.No)
                    return;
            } // else
            else if (item.Result == ComparisonResult.Deleted)
                return;

            ProgressDialog dlg = new ProgressDialog();
            ItemCopier copier = new ItemCopier(_leftSchema, _rightSchema, item, _leftdb, _rightdb, true);
            dlg.Start(this, copier);
            if (dlg.Error == null)
            {
                // Update the comparison view to reflect the change that was done.
                FixView(item, row, true);

                // Notify about the change so the main form can update its sensitivity
                if (SelectionChanged != null)
                    SelectionChanged(this, EventArgs.Empty);
            }
        }

        /// <summary>
        /// Copies the DB entity stored in the right DB to the left DB
        /// </summary>
        public void CopyFromRightDB()
        {
            if (_adding || grdSchemaDiffs.SelectedRows.Count == 0)
                return;
            DataGridViewRow row = grdSchemaDiffs.SelectedRows[0];
            SchemaComparisonItem item = (SchemaComparisonItem)row.Tag;

            if (item.Result == ComparisonResult.ExistsInRightDB ||
                item.Result == ComparisonResult.DifferentSchema ||
                item.Result == ComparisonResult.DifferentData ||
                item.Result == ComparisonResult.Same)
            {
                DialogResult res = MessageBox.Show(this,
                    "Are you sure you want to copy " + Utils.GetItemObjectTypeName(item) +
                    " " + item.ObjectName + " " +
                    "from the right database to the left datbase?",
                    "Confirm copying",
                    MessageBoxButtons.YesNo,
                    MessageBoxIcon.Warning,
                    MessageBoxDefaultButton.Button2);
                if (res == DialogResult.No)
                    return;
            }
            else if (item.Result == ComparisonResult.ExistsInLeftDB)
            {
                DialogResult res = MessageBox.Show(this,
                    "Are you sure you want to delete " + Utils.GetItemObjectTypeName(item) +
                    " " + item.ObjectName + " " +
                    "from the left database?", "Confirm deletion",
                    MessageBoxButtons.YesNo,
                    MessageBoxIcon.Warning,
                    MessageBoxDefaultButton.Button2);
                if (res == DialogResult.No)
                    return;
            } // else
            else if (item.Result == ComparisonResult.Deleted)
                return;

            ProgressDialog dlg = new ProgressDialog();
            ItemCopier copier = new ItemCopier(_leftSchema, _rightSchema, item, _leftdb, _rightdb, false);
            dlg.Start(this, copier);
            if (dlg.Error == null)
            {
                // Update the comparison view
                FixView(item, row, false);

                // Notify about the change so the main form can update its sensitivity
                if (SelectionChanged != null)
                    SelectionChanged(this, EventArgs.Empty);
            }
        }

        /// <summary>
        /// Returns TRUE if the user can copy the DB entity from the right database
        /// to the left database.
        /// </summary>
        public bool CanCopyFromRightDB()
        {
            if (_adding || grdSchemaDiffs.SelectedRows.Count == 0)
                return false;
            DataGridViewRow row = grdSchemaDiffs.SelectedRows[0];
            if (!row.Visible)
                return false;

            SchemaComparisonItem item = (SchemaComparisonItem)row.Tag;
            if (item.LeftDdlStatement == null && item.RightDdlStatement == null)
                return false;

            return true;
        }

        /// <summary>
        /// Returns TRUE if the user can edit the selected different DB entities
        /// </summary>
        public bool CanEditSelectedDifference()
        {
            if (_adding || grdSchemaDiffs.SelectedRows.Count == 0)
                return false;
            DataGridViewRow row = grdSchemaDiffs.SelectedRows[0];
            if (!row.Visible)
                return false;

            SchemaComparisonItem item = (SchemaComparisonItem)row.Tag;
            if (item.LeftDdlStatement == null && item.RightDdlStatement == null)
                return false;

            if (!(item.Result == ComparisonResult.ExistsInLeftDB || item.Result == ComparisonResult.ExistsInRightDB ||
                item.Result == ComparisonResult.Deleted))
                return true;
            return false;
        }

        /// <summary>
        /// Returns TRUE if there are any differences
        /// </summary>
        public bool HasDiffs()
        {
            foreach (DataGridViewRow row in grdSchemaDiffs.Rows)
            {
                SchemaComparisonItem item = row.Tag as SchemaComparisonItem;
                if (item != null && (item.Result != ComparisonResult.Same || (item.TableChanges != null && !item.TableChanges.SameTables)))
                    return true;
            } // foreach
            return false;
        }

        /// <summary>
        /// Returns TRUE if there are any data differences
        /// </summary>
        public bool HasDataDiffs()
        {
            foreach (DataGridViewRow row in grdSchemaDiffs.Rows)
            {
                SchemaComparisonItem item = row.Tag as SchemaComparisonItem;
                if (item != null && ((item.TableChanges != null && !item.TableChanges.SameTables)))
                    return true;
            } // foreach
            return false;
        }

        /// <summary>
        /// Opens the compare-dialog in order to compare two DB entities
        /// </summary>
        public void OpenCompareDialog()
        {
            if (grdSchemaDiffs.SelectedRows.Count == 0)
                return;

            DataGridViewRow row = grdSchemaDiffs.SelectedRows[0];
            SchemaComparisonItem item = (SchemaComparisonItem)row.Tag;
            if (item.LeftDdlStatement == null || item.RightDdlStatement == null)
            {
                MessageBox.Show(this,
                    "Can't compare objects because one of the objects exist in only one of the database files.",
                    "Comparison Failed",
                    MessageBoxButtons.OK,
                    MessageBoxIcon.Exclamation);
                return;
            }

            TwoWayCompareEditDialog dlg = new TwoWayCompareEditDialog();
            dlg.Prepare(item, _leftSchema, _rightSchema, _leftdb, _rightdb);
            dlg.SchemaChanged += new EventHandler(dlg_SchemaChanged);
            dlg.ShowDialog(this);
            dlg.SchemaChanged -= new EventHandler(dlg_SchemaChanged);
        }

        public void ExportDataDifferences()
        {
            ExportChangesDialog dlg = new ExportChangesDialog();
            dlg.MultipleChanges = _results[SchemaObject.Table];
            dlg.ShowDialog(this);
        }
        #endregion

        #region Public Properties
        /// <summary>
        /// Return the results object
        /// </summary>
        public Dictionary<SchemaObject, List<SchemaComparisonItem>> Results
        {
            get { return _results; }
        }
        #endregion

        #region Event Handlers
        private void dlg_SchemaChanged(object sender, EventArgs e)
        {
            DataGridViewRow row = grdSchemaDiffs.SelectedRows[0];
            SchemaComparisonItem item = (SchemaComparisonItem)row.Tag;
            FormatRow(row);

            bool hasData = false;
            foreach (DataGridViewRow r in grdSchemaDiffs.Rows)
            {
                SchemaComparisonItem i = (SchemaComparisonItem)r.Tag;
                if (i.TableChanges != null)
                {
                    hasData = true;
                    break;
                }
            } // foreach
            lblTableDataIsDifferent.Visible = hasData;
            UpdateChangesCount();
        }

        private void cbxShowOnlyDifferences_CheckedChanged(object sender, EventArgs e)
        {
            ShowMatchingRows();
        }

        private void cbxShowTableDifferences_CheckedChanged(object sender, EventArgs e)
        {
            ShowMatchingRows();
        }

        private void cbxShowIndexDifferences_CheckedChanged(object sender, EventArgs e)
        {
            ShowMatchingRows();
        }

        private void cbxShowViewDifferences_CheckedChanged(object sender, EventArgs e)
        {
            ShowMatchingRows();
        }

        private void cbxShowTriggerDifferences_CheckedChanged(object sender, EventArgs e)
        {
            ShowMatchingRows();
        }

        private void txtSearch_TextChanged(object sender, EventArgs e)
        {
            ShowMatchingRows();
        }

        private void grdSchemaDiffs_SelectionChanged(object sender, EventArgs e)
        {
            if (SelectionChanged != null)
                SelectionChanged(this, EventArgs.Empty);
        }

        private void grdSchemaDiffs_CellDoubleClick(object sender, DataGridViewCellEventArgs e)
        {
            if (e.RowIndex < 0)
                return;

            DataGridViewRow row = grdSchemaDiffs.Rows[e.RowIndex];
            SchemaComparisonItem item = (SchemaComparisonItem)row.Tag;
            if (item.LeftDdlStatement == null || item.RightDdlStatement == null)
            {
                MessageBox.Show(this,
                    "Can't compare objects because one of the objects exist in only one of the database files.",
                    "Comparison Failed",
                    MessageBoxButtons.OK,
                    MessageBoxIcon.Exclamation);
                return;
            }

            OpenCompareDialog();
        }
        #endregion

        #region Private Methods

        private void UpdateChangesCount()
        {
            int count = 0;
            foreach (SchemaObject objtype in _results.Keys)
            {
                foreach (SchemaComparisonItem item in _results[objtype])
                {
                    if (item.Result != ComparisonResult.Same || (item.TableChanges != null && !item.TableChanges.SameTables))
                        count++;
                } // foreach
            } // foreach

            // Update the number of changes still left                
            lblTotalFound.Text = "" + count + " changes found";
        }

        private void FixView(SchemaComparisonItem item, DataGridViewRow row, bool leftToRight)
        {
            if (leftToRight)
                FixLeftToRight(item, row);
            else
                FixRightToLeft(item, row);
        }

        private void FixRightToLeft(SchemaComparisonItem item, DataGridViewRow row)
        {
            SQLiteDdlStatement orig = item.LeftDdlStatement;

            if (item.RightDdlStatement != null)
                item.LeftDdlStatement = (SQLiteDdlStatement)item.RightDdlStatement.Clone();
            else
                item.LeftDdlStatement = null;

            if (item.Result != ComparisonResult.Same || (item.TableChanges != null && !item.TableChanges.SameTables))
            {
                item.TableChanges = null;
                item.Result = ComparisonResult.Same;
            }

            // Replace the existing schema object with the new one (taken from the right
            // database schema).
            if (orig != null)
            {
                if (orig is SQLiteCreateIndexStatement)
                    _leftSchema[SchemaObject.Index].Remove(orig.ObjectName.ToString().ToLower());
                else if (orig is SQLiteCreateTableStatement)
                    _leftSchema[SchemaObject.Table].Remove(orig.ObjectName.ToString().ToLower());
                else if (orig is SQLiteCreateTriggerStatement)
                    _leftSchema[SchemaObject.Trigger].Remove(orig.ObjectName.ToString().ToLower());
                else if (orig is SQLiteCreateViewStatement)
                    _leftSchema[SchemaObject.View].Remove(orig.ObjectName.ToString().ToLower());
            }
            if (item.LeftDdlStatement != null)
            {
                if (item.LeftDdlStatement is SQLiteCreateIndexStatement)
                {
                    _leftSchema[SchemaObject.Index].Add(item.LeftDdlStatement.ObjectName.ToString().ToLower(),
                        item.RightDdlStatement);
                }
                else if (item.LeftDdlStatement is SQLiteCreateTableStatement)
                {
                    _leftSchema[SchemaObject.Table].Add(item.LeftDdlStatement.ObjectName.ToString().ToLower(),
                        item.LeftDdlStatement);
                }
                else if (item.LeftDdlStatement is SQLiteCreateTriggerStatement)
                {
                    _leftSchema[SchemaObject.Trigger].Add(item.LeftDdlStatement.ObjectName.ToString().ToLower(),
                        item.LeftDdlStatement);
                }
                else if (item.LeftDdlStatement is SQLiteCreateViewStatement)
                {
                    _leftSchema[SchemaObject.View].Add(item.LeftDdlStatement.ObjectName.ToString().ToLower(),
                        item.LeftDdlStatement);
                }
            }

            // Re-format the comparison row
            FormatRow(row);

            // Special treatment is required if the copied entity was a table. This is so
            // because when deleting a table - all of its associated triggers and indexes
            // are deleted as well, and when copying a table - all of its associated 
            // triggers and indexes are replacing the indexes and triggers in the target schema.

            if (item.LeftDdlStatement == null && orig != null && orig is SQLiteCreateTableStatement)
            {
                // The table originally existed in the right database schema but was deleted (the
                // left database don't contain this table nor any of its indexes or triggers).

                SQLiteCreateTableStatement origTable = orig as SQLiteCreateTableStatement;

                // The table was deleted - mark all associated triggers and indexes as deleted
                foreach (DataGridViewRow crow in grdSchemaDiffs.Rows)
                {
                    SchemaComparisonItem sci = (SchemaComparisonItem)crow.Tag;
                    SQLiteCreateIndexStatement index = sci.LeftDdlStatement as SQLiteCreateIndexStatement;
                    SQLiteCreateTriggerStatement trigger = sci.LeftDdlStatement as SQLiteCreateTriggerStatement;
                    if (index != null && index.OnTable.ToLower() == origTable.ObjectName.ToString().ToLower())
                    {
                        // This index should be marked as removed from the schema in the right database
                        sci.Result = ComparisonResult.Same;
                        sci.RightDdlStatement = null;
                        FormatRow(crow);

                        // Remove the index from the right schema
                        _leftSchema[SchemaObject.Index].Remove(index.ObjectName.ToString().ToLower());
                    } // if
                    else if (trigger != null && trigger.TableName.ToString().ToLower() == origTable.ObjectName.ToString().ToLower())
                    {
                        // This trigger should be marked as removed from the schema in the right database
                        sci.Result = ComparisonResult.Same;
                        sci.RightDdlStatement = null;
                        FormatRow(crow);

                        // Remove the trigger from the right schema
                        _leftSchema[SchemaObject.Trigger].Remove(trigger.ObjectName.ToString().ToLower());
                    } // else
                } // foreach
            }
            else if (item.LeftDdlStatement != null && orig != null && orig is SQLiteCreateTableStatement)
            {
                // The table originally existed in the right database schema and was replaced by the table
                // from the left database schema. In this case we need to add all indexes/triggers that
                // belong exclusively to the table from the left database schema and remove all indexes/triggers that
                // belong exclusively to the original table in the right database.

                SQLiteCreateTableStatement origTable = orig as SQLiteCreateTableStatement;
                SQLiteCreateTableStatement updTable = item.LeftDdlStatement as SQLiteCreateTableStatement;

                List<SQLiteCreateIndexStatement> indexesToRemove =
                    ComputeIndexesExclusiveToTable(origTable, _leftSchema, _rightSchema);
                foreach (DataGridViewRow crow in grdSchemaDiffs.Rows)
                {
                    SchemaComparisonItem sci = (SchemaComparisonItem)crow.Tag;
                    SQLiteCreateIndexStatement index = sci.LeftDdlStatement as SQLiteCreateIndexStatement;
                    if (index != null && FindIndexInList(index, indexesToRemove))
                    {
                        // Mark this index as removed.
                        sci.Result = ComparisonResult.Same;
                        sci.LeftDdlStatement = null;
                        FormatRow(crow);

                        // Remove the index from the right database schema
                        _leftSchema[SchemaObject.Index].Remove(index.ObjectName.ToString().ToLower());
                    }

                } // foreach

                List<SQLiteCreateIndexStatement> indexesToAdd =
                    ComputeIndexesExclusiveToTable(updTable, _rightSchema, _leftSchema);
                foreach (DataGridViewRow crow in grdSchemaDiffs.Rows)
                {
                    SchemaComparisonItem sci = (SchemaComparisonItem)crow.Tag;
                    SQLiteCreateIndexStatement index = sci.RightDdlStatement as SQLiteCreateIndexStatement;
                    if (index != null && FindIndexInList(index, indexesToAdd))
                    {
                        // Mark this index as added to the right database
                        sci.Result = ComparisonResult.Same;
                        sci.LeftDdlStatement = (SQLiteDdlStatement)index.Clone();
                        FormatRow(crow);

                        // Add the index to the right database schema
                        SQLiteCreateIndexStatement indexCopy = (SQLiteCreateIndexStatement)sci.LeftDdlStatement;
                        _leftSchema[SchemaObject.Index].Add(indexCopy.ObjectName.ToString().ToLower(), indexCopy);
                    }
                } // foreach

                List<SQLiteCreateTriggerStatement> triggersToRemove =
                    ComputeTriggersExclusiveToTable(origTable, _leftSchema, _rightSchema);
                foreach (DataGridViewRow crow in grdSchemaDiffs.Rows)
                {
                    SchemaComparisonItem sci = (SchemaComparisonItem)crow.Tag;
                    SQLiteCreateTriggerStatement trigger = sci.LeftDdlStatement as SQLiteCreateTriggerStatement;
                    if (trigger != null && FindTriggerInList(trigger, triggersToRemove))
                    {
                        // Mark this trigger as removed from the right database schema
                        sci.Result = ComparisonResult.Same;
                        sci.LeftDdlStatement = null;
                        FormatRow(crow);

                        // Remove the table trigger from the right database schema
                        _leftSchema[SchemaObject.Trigger].Remove(trigger.ObjectName.ToString().ToLower());
                    }

                } // foreach

                List<SQLiteCreateTriggerStatement> triggersToAdd =
                    ComputeTriggersExclusiveToTable(updTable, _rightSchema, _leftSchema);
                foreach (DataGridViewRow crow in grdSchemaDiffs.Rows)
                {
                    SchemaComparisonItem sci = (SchemaComparisonItem)crow.Tag;
                    SQLiteCreateTriggerStatement trigger = sci.RightDdlStatement as SQLiteCreateTriggerStatement;
                    if (trigger != null && FindTriggerInList(trigger, triggersToAdd))
                    {
                        // Mark this trigger as added to the right database schema
                        sci.Result = ComparisonResult.Same;
                        sci.LeftDdlStatement = (SQLiteDdlStatement)trigger.Clone();
                        FormatRow(crow);

                        // Add this trigger to the right database schema
                        SQLiteCreateTriggerStatement triggerCopy = (SQLiteCreateTriggerStatement)sci.LeftDdlStatement;
                        _leftSchema[SchemaObject.Trigger].Add(triggerCopy.ObjectName.ToString().ToLower(), triggerCopy);
                    }
                } // foreach
            } // else
            else if (orig == null && item.LeftDdlStatement != null && item.LeftDdlStatement is SQLiteCreateTableStatement)
            {
                // The table did not exist in the right database, but was added from the left database.
                // In this case - we need to mark that all of the indexes and triggers of the left database
                // were added to the right database.

                SQLiteCreateTableStatement updTable = item.LeftDdlStatement as SQLiteCreateTableStatement;
                foreach (DataGridViewRow crow in grdSchemaDiffs.Rows)
                {
                    SchemaComparisonItem sci = (SchemaComparisonItem)crow.Tag;
                    SQLiteCreateIndexStatement index = sci.RightDdlStatement as SQLiteCreateIndexStatement;
                    SQLiteCreateTriggerStatement trigger = sci.RightDdlStatement as SQLiteCreateTriggerStatement;
                    if (index != null && index.OnTable.ToLower() == updTable.ObjectName.ToString().ToLower())
                    {
                        // This index should be mared as added to the right database
                        sci.Result = ComparisonResult.Same;
                        sci.LeftDdlStatement = (SQLiteDdlStatement)index.Clone();
                        FormatRow(crow);

                        // Add the index to the right database schema
                        SQLiteCreateIndexStatement indexCopy = (SQLiteCreateIndexStatement)sci.LeftDdlStatement;
                        _leftSchema[SchemaObject.Index].Add(indexCopy.ObjectName.ToString().ToLower(), indexCopy);
                    }
                    else if (trigger != null && trigger.TableName.ToString().ToLower() == updTable.ObjectName.ToString().ToLower())
                    {
                        // This trigger should be marked as added to the right database
                        sci.Result = ComparisonResult.Same;
                        sci.LeftDdlStatement = (SQLiteDdlStatement)trigger.Clone();
                        FormatRow(crow);

                        // Add the trigger to the right database schema
                        SQLiteCreateTriggerStatement triggerCopy = (SQLiteCreateTriggerStatement)sci.LeftDdlStatement;
                        _leftSchema[SchemaObject.Trigger].Add(triggerCopy.ObjectName.ToString().ToLower(), triggerCopy);
                    } // else                        
                } // foreach
            } // else

            // Update the number of changes still left                
            UpdateChangesCount();
        }

        private void FixLeftToRight(SchemaComparisonItem item, DataGridViewRow row)
        {
            SQLiteDdlStatement orig = item.RightDdlStatement;

            if (item.LeftDdlStatement != null)
                item.RightDdlStatement = (SQLiteDdlStatement)item.LeftDdlStatement.Clone();
            else
                item.RightDdlStatement = null;

            if (item.Result != ComparisonResult.Same || (item.TableChanges != null && !item.TableChanges.SameTables))
            {
                item.TableChanges = null;
                item.Result = ComparisonResult.Same;
            }

            // Replace the existing schema object with the new one (taken from the left
            // database schema).
            if (orig != null)
            {
                if (orig is SQLiteCreateIndexStatement)
                    _rightSchema[SchemaObject.Index].Remove(orig.ObjectName.ToString().ToLower());
                else if (orig is SQLiteCreateTableStatement)
                    _rightSchema[SchemaObject.Table].Remove(orig.ObjectName.ToString().ToLower());
                else if (orig is SQLiteCreateTriggerStatement)
                    _rightSchema[SchemaObject.Trigger].Remove(orig.ObjectName.ToString().ToLower());
                else if (orig is SQLiteCreateViewStatement)
                    _rightSchema[SchemaObject.View].Remove(orig.ObjectName.ToString().ToLower());
            }
            if (item.RightDdlStatement != null)
            {
                if (item.RightDdlStatement is SQLiteCreateIndexStatement)
                {
                    _rightSchema[SchemaObject.Index].Add(item.RightDdlStatement.ObjectName.ToString().ToLower(),
                        item.RightDdlStatement);
                }
                else if (item.RightDdlStatement is SQLiteCreateTableStatement)
                {
                    _rightSchema[SchemaObject.Table].Add(item.RightDdlStatement.ObjectName.ToString().ToLower(),
                        item.RightDdlStatement);
                }
                else if (item.RightDdlStatement is SQLiteCreateTriggerStatement)
                {
                    _rightSchema[SchemaObject.Trigger].Add(item.RightDdlStatement.ObjectName.ToString().ToLower(),
                        item.RightDdlStatement);
                }
                else if (item.RightDdlStatement is SQLiteCreateViewStatement)
                {
                    _rightSchema[SchemaObject.View].Add(item.RightDdlStatement.ObjectName.ToString().ToLower(),
                        item.RightDdlStatement);
                }
            }

            // Re-format the comparison row
            FormatRow(row);

            // Special treatment is required if the copied entity was a table. This is so
            // because when deleting a table - all of its associated triggers and indexes
            // are deleted as well, and when copying a table - all of its associated 
            // triggers and indexes are replacing the indexes and triggers in the target schema.

            if (item.RightDdlStatement == null && orig != null && orig is SQLiteCreateTableStatement)
            {
                // The table originally existed in the right database schema but was deleted (the
                // left database don't contain this table nor any of its indexes or triggers).

                SQLiteCreateTableStatement origTable = orig as SQLiteCreateTableStatement;

                // The table was deleted - mark all associated triggers and indexes as deleted
                foreach (DataGridViewRow crow in grdSchemaDiffs.Rows)
                {
                    SchemaComparisonItem sci = (SchemaComparisonItem)crow.Tag;
                    SQLiteCreateIndexStatement index = sci.RightDdlStatement as SQLiteCreateIndexStatement;
                    SQLiteCreateTriggerStatement trigger = sci.RightDdlStatement as SQLiteCreateTriggerStatement;
                    if (index != null && index.OnTable.ToLower() == origTable.ObjectName.ToString().ToLower())
                    {
                        // This index should be marked as removed from the schema in the right database
                        sci.Result = ComparisonResult.Same;
                        sci.RightDdlStatement = null;
                        FormatRow(crow);

                        // Remove the index from the right schema
                        _rightSchema[SchemaObject.Index].Remove(index.ObjectName.ToString().ToLower());
                    } // if
                    else if (trigger != null && trigger.TableName.ToString().ToLower() == origTable.ObjectName.ToString().ToLower())
                    {
                        // This trigger should be marked as removed from the schema in the right database
                        sci.Result = ComparisonResult.Same;
                        sci.RightDdlStatement = null;
                        FormatRow(crow);

                        // Remove the trigger from the right schema
                        _rightSchema[SchemaObject.Trigger].Remove(trigger.ObjectName.ToString().ToLower());
                    } // else
                } // foreach
            }
            else if (item.RightDdlStatement != null && orig != null && orig is SQLiteCreateTableStatement)
            {
                // The table originally existed in the right database schema and was replaced by the table
                // from the left database schema. In this case we need to add all indexes/triggers that
                // belong exclusively to the table from the left database schema and remove all indexes/triggers that
                // belong exclusively to the original table in the right database.

                SQLiteCreateTableStatement origTable = orig as SQLiteCreateTableStatement;
                SQLiteCreateTableStatement updTable = item.RightDdlStatement as SQLiteCreateTableStatement;

                List<SQLiteCreateIndexStatement> indexesToRemove =
                    ComputeIndexesExclusiveToTable(origTable, _rightSchema, _leftSchema);
                foreach (DataGridViewRow crow in grdSchemaDiffs.Rows)
                {
                    SchemaComparisonItem sci = (SchemaComparisonItem)crow.Tag;
                    SQLiteCreateIndexStatement index = sci.RightDdlStatement as SQLiteCreateIndexStatement;
                    if (index != null && FindIndexInList(index, indexesToRemove))
                    {
                        // Mark this index as removed.
                        sci.Result = ComparisonResult.Same;
                        sci.RightDdlStatement = null;
                        FormatRow(crow);

                        // Remove the index from the right database schema
                        _rightSchema[SchemaObject.Index].Remove(index.ObjectName.ToString().ToLower());
                    }

                } // foreach

                List<SQLiteCreateIndexStatement> indexesToAdd =
                    ComputeIndexesExclusiveToTable(updTable, _leftSchema, _rightSchema);
                foreach (DataGridViewRow crow in grdSchemaDiffs.Rows)
                {
                    SchemaComparisonItem sci = (SchemaComparisonItem)crow.Tag;
                    SQLiteCreateIndexStatement index = sci.LeftDdlStatement as SQLiteCreateIndexStatement;
                    if (index != null && FindIndexInList(index, indexesToAdd))
                    {
                        // Mark this index as added to the right database
                        sci.Result = ComparisonResult.Same;
                        sci.RightDdlStatement = (SQLiteDdlStatement)index.Clone();
                        FormatRow(crow);

                        // Add the index to the right database schema
                        SQLiteCreateIndexStatement indexCopy = (SQLiteCreateIndexStatement)sci.RightDdlStatement;
                        _rightSchema[SchemaObject.Index].Add(indexCopy.ObjectName.ToString().ToLower(), indexCopy);
                    }
                } // foreach

                List<SQLiteCreateTriggerStatement> triggersToRemove =
                    ComputeTriggersExclusiveToTable(origTable, _rightSchema, _leftSchema);
                foreach (DataGridViewRow crow in grdSchemaDiffs.Rows)
                {
                    SchemaComparisonItem sci = (SchemaComparisonItem)crow.Tag;
                    SQLiteCreateTriggerStatement trigger = sci.RightDdlStatement as SQLiteCreateTriggerStatement;
                    if (trigger != null && FindTriggerInList(trigger, triggersToRemove))
                    {
                        // Mark this trigger as removed from the right database schema
                        sci.Result = ComparisonResult.Same;
                        sci.RightDdlStatement = null;
                        FormatRow(crow);

                        // Remove the table trigger from the right database schema
                        _rightSchema[SchemaObject.Trigger].Remove(trigger.ObjectName.ToString().ToLower());
                    }

                } // foreach

                List<SQLiteCreateTriggerStatement> triggersToAdd =
                    ComputeTriggersExclusiveToTable(updTable, _leftSchema, _rightSchema);
                foreach (DataGridViewRow crow in grdSchemaDiffs.Rows)
                {
                    SchemaComparisonItem sci = (SchemaComparisonItem)crow.Tag;
                    SQLiteCreateTriggerStatement trigger = sci.LeftDdlStatement as SQLiteCreateTriggerStatement;
                    if (trigger != null && FindTriggerInList(trigger, triggersToAdd))
                    {
                        // Mark this trigger as added to the right database schema
                        sci.Result = ComparisonResult.Same;
                        sci.RightDdlStatement = (SQLiteDdlStatement)trigger.Clone();
                        FormatRow(crow);

                        // Add this trigger to the right database schema
                        SQLiteCreateTriggerStatement triggerCopy = (SQLiteCreateTriggerStatement)sci.RightDdlStatement;
                        _rightSchema[SchemaObject.Trigger].Add(triggerCopy.ObjectName.ToString().ToLower(), triggerCopy);
                    }
                } // foreach
            } // else
            else if (orig == null && item.RightDdlStatement != null && item.RightDdlStatement is SQLiteCreateTableStatement)
            {
                // The table did not exist in the right database, but was added from the left database.
                // In this case - we need to mark that all of the indexes and triggers of the left database
                // were added to the right database.

                SQLiteCreateTableStatement updTable = item.RightDdlStatement as SQLiteCreateTableStatement;
                foreach (DataGridViewRow crow in grdSchemaDiffs.Rows)
                {
                    SchemaComparisonItem sci = (SchemaComparisonItem)crow.Tag;
                    SQLiteCreateIndexStatement index = sci.LeftDdlStatement as SQLiteCreateIndexStatement;
                    SQLiteCreateTriggerStatement trigger = sci.LeftDdlStatement as SQLiteCreateTriggerStatement;
                    if (index != null && index.OnTable.ToLower() == updTable.ObjectName.ToString().ToLower())
                    {
                        // This index should be mared as added to the right database
                        sci.Result = ComparisonResult.Same;
                        sci.RightDdlStatement = (SQLiteDdlStatement)index.Clone();
                        FormatRow(crow);

                        // Add the index to the right database schema
                        SQLiteCreateIndexStatement indexCopy = (SQLiteCreateIndexStatement)sci.RightDdlStatement;
                        _rightSchema[SchemaObject.Index].Add(indexCopy.ObjectName.ToString().ToLower(), indexCopy);
                    }
                    else if (trigger != null && trigger.TableName.ToString().ToLower() == updTable.ObjectName.ToString().ToLower())
                    {
                        // This trigger should be marked as added to the right database
                        sci.Result = ComparisonResult.Same;
                        sci.RightDdlStatement = (SQLiteDdlStatement)trigger.Clone();
                        FormatRow(crow);

                        // Add the trigger to the right database schema
                        SQLiteCreateTriggerStatement triggerCopy = (SQLiteCreateTriggerStatement)sci.RightDdlStatement;
                        _rightSchema[SchemaObject.Trigger].Add(triggerCopy.ObjectName.ToString().ToLower(), triggerCopy);
                    } // else                        
                } // foreach
            } // else

            // Update the number of changes still left                
            UpdateChangesCount();
        }

        private List<SQLiteCreateIndexStatement> ComputeIndexesExclusiveToTable(
            SQLiteCreateTableStatement table1, 
            Dictionary<SchemaObject, Dictionary<string, SQLiteDdlStatement>> schema1,             
            Dictionary<SchemaObject, Dictionary<string, SQLiteDdlStatement>> schema2)
        {
            List<SQLiteCreateIndexStatement> res = new List<SQLiteCreateIndexStatement>();
            foreach (SQLiteCreateIndexStatement index in schema1[SchemaObject.Index].Values)
            {
                if (index.OnTable.ToLower() == table1.ObjectName.ToString().ToLower())
                {
                    bool found = false;
                    foreach (SQLiteCreateIndexStatement index2 in schema2[SchemaObject.Index].Values)
                    {
                        if (index2.OnTable.ToLower() == table1.ObjectName.ToString().ToLower() &&
                            index2.ObjectName.ToString().ToLower() == index.ObjectName.ToString().ToLower())
                        {
                            found = true;
                            break;
                        }
                    } // foreach
                    if (!found)
                        res.Add(index);
                } // if
            } // foreach
            return res;
        }

        private List<SQLiteCreateTriggerStatement> ComputeTriggersExclusiveToTable(
            SQLiteCreateTableStatement table1,
            Dictionary<SchemaObject, Dictionary<string, SQLiteDdlStatement>> schema1,
            Dictionary<SchemaObject, Dictionary<string, SQLiteDdlStatement>> schema2)
        {
            List<SQLiteCreateTriggerStatement> res = new List<SQLiteCreateTriggerStatement>();
            foreach (SQLiteCreateTriggerStatement trigger in schema1[SchemaObject.Trigger].Values)
            {
                if (trigger.TableName.ToString().ToLower() == table1.ObjectName.ToString().ToLower())
                {
                    bool found = false;
                    foreach (SQLiteCreateTriggerStatement trigger2 in schema2[SchemaObject.Trigger].Values)
                    {
                        if (trigger2.TableName.ToString().ToLower() == table1.ObjectName.ToString().ToLower() &&
                            trigger2.ObjectName.ToString().ToLower() == trigger.ObjectName.ToString().ToLower())
                        {
                            found = true;
                            break;
                        }
                    } // foreach
                    if (!found)
                        res.Add(trigger);
                } // if
            } // foreach
            return res;
        }

        private bool FindIndexInList(SQLiteCreateIndexStatement index, List<SQLiteCreateIndexStatement> ilist)
        {
            foreach (SQLiteCreateIndexStatement ci in ilist)
            {
                if (ci.ObjectName.ToString().ToLower() == index.ObjectName.ToString().ToLower())
                    return true;
            } // foreach
            return false;
        }

        private bool FindTriggerInList(SQLiteCreateTriggerStatement trigger, List<SQLiteCreateTriggerStatement> ilist)
        {
            foreach (SQLiteCreateTriggerStatement ci in ilist)
            {
                if (ci.ObjectName.ToString().ToLower() == trigger.ObjectName.ToString().ToLower())
                    return true;
            } // foreach
            return false;
        }

        private void UpdateView()
        {
            ItemComparer comparer = new ItemComparer();
            grdSchemaDiffs.Rows.Clear();

            List<SchemaComparisonItem> items = _results[SchemaObject.Table];
            items.Sort(comparer);
            AddComparisonItemsToGrid(items);
            items = _results[SchemaObject.Index];
            items.Sort(comparer);
            AddComparisonItemsToGrid(items);
            items = _results[SchemaObject.View];
            items.Sort(comparer);
            AddComparisonItemsToGrid(items);
            items = _results[SchemaObject.Trigger];
            items.Sort(comparer);
            AddComparisonItemsToGrid(items);

            // Clear selection
            grdSchemaDiffs.ClearSelection();

            UpdateChangesCount();
        }

        private void FormatRow(DataGridViewRow row)
        {
            SchemaComparisonItem item = (SchemaComparisonItem)row.Tag;

            row.Cells[2].Style.Font = this.Font;
            row.Cells[3].Style.Font = this.Font;
            row.Cells[2].Style.BackColor = NORMAL_BGCOLOR;
            row.Cells[3].Style.BackColor = NORMAL_BGCOLOR;
            row.Cells[2].Value = item.ObjectName;
            row.Cells[3].Value = item.ObjectName;

            if (item.ErrorMessage != null)
            {
                row.Cells[2].Style.BackColor = COMPARISON_ERROR_COLOR;
                row.Cells[3].Style.BackColor = COMPARISON_ERROR_COLOR;
            }
            else if (item.TableChanges != null && !item.TableChanges.SameTables)
            {
                row.Cells[2].Style.BackColor = DIFFERENT_DATA_COLOR;
                row.Cells[3].Style.BackColor = DIFFERENT_DATA_COLOR;
            }
            else if (item.Result == ComparisonResult.ExistsInLeftDB)
            {
                row.Cells[3].Style.Font = _strikeout;
                row.Cells[3].Style.BackColor = NOT_EXIST_COLOR;
            }
            else if (item.Result == ComparisonResult.ExistsInRightDB)
            {
                row.Cells[2].Style.Font = _strikeout;
                row.Cells[2].Style.BackColor = NOT_EXIST_COLOR;
            }
            else if (item.Result == ComparisonResult.DifferentSchema)
            {
                row.Cells[2].Style.BackColor = DIFFERENT_SCHEMA_COLOR;
                row.Cells[3].Style.BackColor = DIFFERENT_SCHEMA_COLOR;
            }
            else if (item.Result == ComparisonResult.Same)
            {
                if (item.LeftDdlStatement == null)
                {
                    row.Cells[2].Style.Font = _strikeout;
                    row.Cells[2].Style.BackColor = NOT_EXIST_COLOR;
                    row.Cells[3].Style.Font = _strikeout;
                    row.Cells[3].Style.BackColor = NOT_EXIST_COLOR;
                }
            }
        }

        private void AddComparisonItemsToGrid(List<SchemaComparisonItem> items)
        {
            try
            {
                _adding = true;

                foreach (SchemaComparisonItem item in items)
                {
                    SQLiteDdlStatement stmt = item.LeftDdlStatement;
                    if (stmt == null)
                        stmt = item.RightDdlStatement;

                    grdSchemaDiffs.Rows.Add(GetItemImage(item), GetItemType(item), SQLiteParser.Utils.Chop(item.ObjectName),
                        SQLiteParser.Utils.Chop(item.ObjectName));                    
                    DataGridViewRow row = grdSchemaDiffs.Rows[grdSchemaDiffs.Rows.Count - 1];
                    row.Tag = item;

                    FormatRow(row);
                } // foreach
            }
            finally
            {
                _adding = false;
            } // finally
        }

        private void ShowMatchingRows()
        {
            string value = txtSearch.Text.Trim();
            foreach (DataGridViewRow row in grdSchemaDiffs.Rows)
            {
                SchemaComparisonItem item = (SchemaComparisonItem)row.Tag;

                SQLiteDdlStatement stmt = item.LeftDdlStatement;
                if (stmt == null)
                    stmt = item.RightDdlStatement;

                bool match = stmt is SQLiteCreateTableStatement && cbxShowTableDifferences.Checked ||
                    stmt is SQLiteCreateIndexStatement && cbxShowIndexDifferences.Checked ||
                    stmt is SQLiteCreateViewStatement && cbxShowViewDifferences.Checked ||
                    stmt is SQLiteCreateTriggerStatement && cbxShowTriggerDifferences.Checked;
                bool diff = (cbxShowOnlyDifferences.Checked && 
                    (item.Result != ComparisonResult.Same || (item.TableChanges != null && !item.TableChanges.SameTables))) || 
                    !cbxShowOnlyDifferences.Checked;
                match = match && diff;

                string name = (string)row.Cells[2].Value;
                if (value == string.Empty)
                    row.Visible = match;
                row.Visible = name.ToLower().Contains(value.ToLower()) && match;
            } // foreach

            if (SelectionChanged != null)
                SelectionChanged(this, EventArgs.Empty);
        }

        private string GetItemType(SchemaComparisonItem item)
        {
            SQLiteDdlStatement stmt = item.LeftDdlStatement;
            if (stmt == null)
                stmt = item.RightDdlStatement;

            if (stmt is SQLiteCreateTableStatement)
                return "Table";
            else if (stmt is SQLiteCreateIndexStatement)
                return "Index";
            else if (stmt is SQLiteCreateViewStatement)
                return "View";
            else if (stmt is SQLiteCreateTriggerStatement)
                return "Trigger";
            else
                throw new ArgumentException("Illegal item type");
        }

        private Image GetItemImage(SchemaComparisonItem item)
        {
            SQLiteDdlStatement stmt = item.LeftDdlStatement;
            if (stmt == null)
                stmt = item.RightDdlStatement;

            if (stmt is SQLiteCreateTableStatement)
                return imageList1.Images["db_table"];
            else if (stmt is SQLiteCreateIndexStatement)
                return imageList1.Images["db_index"];
            else if (stmt is SQLiteCreateViewStatement)
                return imageList1.Images["db_view"];
            else if (stmt is SQLiteCreateTriggerStatement)
                return imageList1.Images["db_trigger"];
            else
                throw new ArgumentException("illegal item type");
        }
        #endregion

        #region Private Constants
        private Color NORMAL_BGCOLOR = SystemColors.Window;
        private Color NOT_EXIST_COLOR = Color.LightGray;
        private Color DIFFERENT_SCHEMA_COLOR = Color.Khaki;
        private Color DIFFERENT_DATA_COLOR = Color.LightBlue;
        private Color COMPARISON_ERROR_COLOR = Color.LightCoral;
        #endregion

        #region Private Classes
        private class ItemComparer : Comparer<SchemaComparisonItem>
        {
            public override int Compare(SchemaComparisonItem x, SchemaComparisonItem y)
            {
                string xname, yname;

                xname = x.ObjectName;
                yname = y.ObjectName;

                return xname.ToLower().CompareTo(yname.ToLower());
            }
        }
        #endregion

        #region Private Variables
        private bool _adding = false;
        private string _leftdb = null;
        private string _rightdb = null;
        private Font _strikeout;
        private Dictionary<SchemaObject, List<SchemaComparisonItem>> _results;
        private Dictionary<SchemaObject, Dictionary<string, SQLiteDdlStatement>> _leftSchema;
        private Dictionary<SchemaObject, Dictionary<string, SQLiteDdlStatement>> _rightSchema;
        #endregion
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The GNU Lesser General Public License (LGPLv3)


Written By
Software Developer
Israel Israel
My name is Liron Levi and I'm developing software for fun & profit for 15 years already.

Comments and Discussions