Click here to Skip to main content
15,898,010 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.8K   37.2K   131  
Utility for comparing two SQLite database files for both structure and data
using System;
using System.IO;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using System.Data;
using System.Data.SQLite;
using System.Reflection;
using System.Diagnostics;
using SQLiteParser;

namespace SQLiteTurbo
{
    /// <summary>
    /// General utilities class.
    /// </summary>
    public class Utils
    {
        /// <summary>
        /// Static constructor to initialize static members
        /// </summary>
        static Utils()
        {
            PrepareConverters();
        }

        /// <summary>
        /// Return the version number for the specified database file.
        /// </summary>
        /// <param name="fpath">The path to the database file</param>
        /// <returns>-1 if the file was not recognized as a valid SQLite file, or the version of the
        /// SQLite file</returns>
        public static float GetSQLiteVersion(string fpath)
        {
            int index = 0;
            byte[] buffer = new byte[1024];
            using (FileStream fs = File.Open(fpath, FileMode.Open, FileAccess.Read, FileShare.Read))
            {                
                int res = fs.ReadByte();
                while (res != -1 && res != 0 && index<buffer.Length)
                {
                    buffer[index] = (byte)res;
                    res = fs.ReadByte();
                    index++;
                } // while

                fs.Close();
            } // using

            // A valid SQLite file will have a much lower index value
            if (index == buffer.Length)
                return -1F;
            
            // Convert the bytes array that was read to an ASCII string
            string vstr = Encoding.ASCII.GetString(buffer, 0, index);

            // Check for version 3 and above file format
            Regex rx3 = new Regex(@"SQLite format (\d+(\.\d+)?)");
            Match m = rx3.Match(vstr);
            if (m.Success)
            {
                float res;
                if (float.TryParse(m.Groups[1].Value, out res))
                    return res;
                else
                    return -1F;
            }

            // Check for version 2.x format
            Regex rx2 = new Regex(@"\*\* This file contains an SQLite (\d+(\.\d+)?) database \*\*");
            m = rx2.Match(vstr);
            if (m.Success)
            {
                float res;
                if (float.TryParse(m.Groups[1].Value, out res))
                    return res;
            }

            return -1F;
        }

        /// <summary>
        /// Get the name of the item object type
        /// </summary>
        /// <param name="item">The schema comparison item</param>
        /// <returns>The name of the object type (e.g., "table", "index" etc.)</returns>
        public static string GetItemObjectTypeName(SchemaComparisonItem item)
        {
            SQLiteDdlStatement stmt = item.LeftDdlStatement;
            if (stmt == null)
                stmt = item.RightDdlStatement;
            if (stmt == null)
                return string.Empty;
            if (stmt is SQLiteCreateTableStatement)
                return "table";
            if (stmt is SQLiteCreateIndexStatement)
                return "index";
            if (stmt is SQLiteCreateViewStatement)
                return "view";
            if (stmt is SQLiteCreateTriggerStatement)
                return "trigger";
            return string.Empty;
        }

        /// <summary>
        /// Convenience method for comparing two objects, with support for comparing byte arrays
        /// </summary>
        /// <param name="lval">The left value to compare</param>
        /// <param name="rval">The right value to compare</param>
        /// <returns>TRUE if the two values are equal, FALSE otherwise</returns>
        public static bool ObjectsAreEqual(object lval, object rval)
        {
            if (lval.GetType() != rval.GetType())
                return false;
            if (lval is byte[])
                return Array.Equals(lval, rval);
            return lval.Equals(rval);
        }

        /// <summary>
        /// Fills the parameters of the specified command with values taken from the specified data reader.
        /// </summary>
        /// <param name="cmd">The command whose parameters we need to fill</param>
        /// <param name="clist">The columns list used in the command</param>
        /// <param name="reader">The data reader objcet to use</param>
        public static void FillCommandParameters(SQLiteCommand cmd, List<SQLiteColumnStatement> clist, SQLiteDataReader reader)
        {
            foreach (SQLiteColumnStatement col in clist)
            {
                string cname = SQLiteParser.Utils.Chop(col.ObjectName.ToString());
                string pname = Utils.GetColumnParameterName(cname);
                object val = reader[cname];

                DbType dbtype = Utils.GetDbTypeFromClrType(col, val);
                SQLiteParameter prm = cmd.Parameters[pname];
                if (prm.DbType != dbtype)
                    prm.DbType = dbtype;
                cmd.Parameters[pname].Value = val;
            } // foreach
        }

        /// <summary>
        /// Checks if the specified string is a valid GUID string
        /// </summary>
        /// <param name="candidate">The candidate string to check</param>
        /// <param name="output">In case the string is a valid GUID - the GUID object</param>
        /// <returns>TRUE if valid, FALSE if not.</returns>
        public static bool IsGuid(string candidate, out Guid output)
        {
            bool isValid = false;
            output = Guid.Empty;
            if (candidate != null)
            {
                if (isGuid.IsMatch(candidate))
                {
                    output = new Guid(candidate);
                    isValid = true;
                }
            }

            return isValid;
        }

        /// <summary>
        /// Returns a list that contains columns with the same names as those specified in the <paramref name="names"/> list
        /// but taken from the columns list specified in <paramref name="basis"/> list.
        /// </summary>
        /// <param name="basis">The basis list from which column objects will be returned</param>
        /// <param name="names">A list of columns whose names are used to pick the relevant columns from the basis list.</param>
        /// <returns></returns>
        public static List<SQLiteColumnStatement> GetMatchingColumns(List<SQLiteColumnStatement> basis, List<SQLiteColumnStatement> names)
        {
            List<SQLiteColumnStatement> res = new List<SQLiteColumnStatement>();
            foreach (SQLiteColumnStatement col in names)
            {
                foreach (SQLiteColumnStatement icol in basis)
                {
                    if (col.ObjectName.Equals(icol.ObjectName))
                    {
                        res.Add(icol);
                        break;
                    }
                } // foreach
            } // foreach

            return res;
        }

        /// <summary>
        /// Checks if the specified columns have a matching underlying type
        /// </summary>
        /// <remarks>As an example - nvarchar(50) and cahr(500) have a matching type</remarks>
        /// <param name="leftCol">The left column to compare</param>
        /// <param name="rightCol">The right column to compare</param>
        /// <returns>TRUE if the two columns have a matching type, FALSE otherwise</returns>
        public static bool IsColumnTypesMatching(SQLiteColumnStatement leftCol, SQLiteColumnStatement rightCol)
        {
            DbType ltype = Utils.GetDbType(leftCol.ColumnType);
            DbType rtype = Utils.GetDbType(rightCol.ColumnType);

            if (ltype == DbType.Single && rtype == DbType.Double ||
                ltype == DbType.Double && ltype == DbType.Single)
                return true;
            else
                return ltype == rtype;
        }

        /// <summary>
        /// Returns the path to the license file
        /// </summary>
        /// <returns></returns>
        public static string GetLicenseFilePath()
        {
            string fpath = GetInstallationDirectory() + "\\license.lic";
            return fpath;
        }

        /// <summary>
        /// Get the instalation directory path
        /// </summary>
        public static string GetInstallationDirectory()
        {
            Process proc = Process.GetCurrentProcess();
            string bp = proc.Modules[0].FileName;
            bp = Path.GetDirectoryName(bp);
            return bp;
        }

        /// <summary>
        /// Get the version number of the software
        /// </summary>
        /// <returns></returns>
        public static string GetSoftwareVersion()
        {
            Assembly assembly = Assembly.GetExecutingAssembly();
            string ver = "" + assembly.GetName().Version.Major + "." +
                assembly.GetName().Version.Minor + " p" +
                assembly.GetName().Version.Revision;
            return ver;
        }

        /// <summary>
        /// Get the software build number
        /// </summary>
        /// <returns></returns>
        public static string GetSoftwareBuild()
        {
            Assembly assembly = Assembly.GetExecutingAssembly();
            string ver = "" + assembly.GetName().Version.Build;
            return ver;
        }

        /// <summary>
        /// Used to create a SELECT list of columns whose values are either NULL (if the column
        /// is NULLABLE and has no NON-NULL-DEFAULT value) or a DEFAULT non-null value. This
        /// list is can be used to construct INSERT statements.
        /// Example: INSERT INTO table1 SELECT NULL as extra, (1) AS id
        /// </summary>
        /// <param name="ncols">The list of columns that either have non-null DEFAULT constraint or
        /// are NULLable</param>
        /// <returns>A string that contains the list of columns with their value assignment (either NULL
        /// or a DEFAULT non-null value)</returns>
        public static string BuildNullableOrNonNullConstantDefaultSelectList(List<SQLiteColumnStatement> ncols)
        {
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < ncols.Count; i++)
            {
                SQLiteColumnStatement c = ncols[i];
                if (c.HasNonNullConstDefault)
                {
                    SQLiteDefaultColumnConstraint condef = c.GetDefaultConstraint();
                    sb.Append("(" + condef.ValueString + ") AS " + c.ObjectName.ToString());
                }
                else if (c.IsNullable)
                {
                    sb.Append("NULL AS " + c.ObjectName.ToString());
                }
                else
                    continue;
                if (i < ncols.Count - 1)
                    sb.Append(",");
            } // for
            return sb.ToString();
        }

        public static string BuildCastedSelectList(List<SQLiteColumnStatement> common, List<SQLiteColumnStatement> diffcols)
        {
            StringBuilder sb = new StringBuilder();
            for (int i=0; i<common.Count; i++)
            {
                bool found = false;
                SQLiteColumnStatement col = common[i];
                if (diffcols != null)
                {
                    foreach (SQLiteColumnStatement dcol in diffcols)
                    {
                        if (col.ObjectName.Equals(dcol.ObjectName))
                        {
                            found = true;

                            // The column changed its type so we need to add an appropriate CAST expression
                            sb.Append("CAST(" + col.ObjectName.ToString() + " as " + GetGeneralColumnType(dcol.ColumnType) + ") as " + col.ObjectName.ToString());
                            break;
                        }
                    } // foreach
                }
                if (!found)
                    sb.Append(col.ObjectName.ToString());
                if (i < common.Count - 1)
                    sb.Append(", ");
            } // for
            return sb.ToString();
        }

        private static string GetGeneralColumnType(SQLiteColumnType ctype)
        {
            string tp = ctype.TypeName;
            if (tp == null)
                return "BLOB";
            else if (tp.Contains("int"))
                return "INTEGER";
            else if (tp.Contains("real") || tp.Contains("doub") || tp.Contains("floa"))
                return "FLOAT";
            else if (tp.Contains("char") || tp.Contains("clob") || tp.Contains("text"))
                return "TEXT";
            else 
                return "TEXT";
        }

        /// <summary>
        /// Checks if any of the table indexes has changed/added/removed between the original
        /// version and the updated version.
        /// </summary>
        /// <param name="tableName">The table name to check</param>
        /// <param name="origIndexes">The dictionary that includes all the original table indexes</param>
        /// <param name="updIndexes">The dictionary that includes all the updated table indexes</param>
        /// <param name="changedIndexes">In case changes were found - it will contain the list of 
        /// all indexes that were </param>
        /// <returns></returns>
        public static bool TableIndexesWereChanged(string tableName, 
            Dictionary<string, SQLiteDdlStatement> origIndexes, 
            Dictionary<string, SQLiteDdlStatement> updIndexes, 
            out List<SQLiteCreateIndexStatement> changedIndexes,
            out List<SQLiteObjectName> removedIndexes)
        {
            changedIndexes = null;
            removedIndexes = null;
            foreach (SQLiteCreateIndexStatement idx in origIndexes.Values)
            {
                if (SQLiteParser.Utils.Chop(idx.OnTable).ToLower() == 
                    SQLiteParser.Utils.Chop(tableName).ToLower())
                {
                    // Go look for the same index in the updated schema. If not found - 
                    // add its naem to the 'removedIndexes' list.
                    bool found = false;
                    foreach (SQLiteCreateIndexStatement idx2 in updIndexes.Values)
                    {
                        if (idx2.ObjectName.Equals(idx.ObjectName))
                        {
                            found = true;

                            // Object was found in the updated schema - check if the two indexes
                            // are equal
                            if (!idx2.Equals(idx))
                            {
                                if (changedIndexes == null)
                                    changedIndexes = new List<SQLiteCreateIndexStatement>();
                                changedIndexes.Add(idx2);
                            }

                            break;
                        }
                    } // foreach

                    if (!found)
                    {
                        // The updated schema does not contain this index so it was removed.
                        if (removedIndexes == null)
                            removedIndexes = new List<SQLiteObjectName>();
                        removedIndexes.Add(idx.ObjectName);
                    } // if
                } // if
            } // foreach

            // Now scan from the direction of the updated schema - see if we can find an index
            // that exists in the updated schema but not in the original schema.
            foreach (SQLiteCreateIndexStatement idx in updIndexes.Values)
            {
                if (SQLiteParser.Utils.Chop(idx.OnTable).ToLower() == 
                    SQLiteParser.Utils.Chop(tableName).ToLower())
                {
                    // Go look for the same index in the original schema. If not found -
                    // add it to the changedIndexes list
                    bool found = false;
                    foreach (SQLiteCreateIndexStatement idx2 in origIndexes.Values)
                    {
                        if (idx2.ObjectName.Equals(idx.ObjectName))
                        {
                            found = true;
                            break;
                        }
                    } // foreach

                    if (!found)
                    {
                        // The original schema does not contain this index so it was added
                        if (changedIndexes == null)
                            changedIndexes = new List<SQLiteCreateIndexStatement>();
                        changedIndexes.Add(idx);
                    } // if
                } // if
            } // foreach

            if (changedIndexes == null && removedIndexes == null)
                return false;
            if (changedIndexes != null && removedIndexes != null)
                return true;
            if (changedIndexes == null)
                changedIndexes = new List<SQLiteCreateIndexStatement>();
            else
                removedIndexes = new List<SQLiteObjectName>();
            return true;
        }

        /// <summary>
        /// Checks if any of the table triggers has changed/added/removed between the original
        /// version and the updated version.
        /// </summary>
        /// <param name="tableName">The table name to check</param>
        /// <param name="origTriggers">The dictionary that includes all the original table indexes</param>
        /// <param name="updTriggers">The dictionary that includes all the updated table indexes</param>
        /// <param name="changedTriggers">In case changes were found - it will contain the list of
        /// all indexes that were</param>
        /// <param name="removedTriggers">The removed triggers.</param>
        /// <returns></returns>
        public static bool TableTriggersWereChanged(string tableName, 
            Dictionary<string, SQLiteDdlStatement> origTriggers, 
            Dictionary<string, SQLiteDdlStatement> updTriggers, 
            out List<SQLiteCreateTriggerStatement> changedTriggers, 
            out List<SQLiteObjectName> removedTriggers)
        {
            changedTriggers = null;
            removedTriggers = null;
            foreach (SQLiteCreateTriggerStatement trg in origTriggers.Values)
            {
                if (SQLiteParser.Utils.Chop(trg.TableName.ToString()).ToLower() == 
                    SQLiteParser.Utils.Chop(tableName).ToLower())
                {
                    // Go look for the same trigger in the updated schema. If not found - 
                    // add its naem to the 'removedTriggers' list.
                    bool found = false;
                    foreach (SQLiteCreateTriggerStatement trg2 in updTriggers.Values)
                    {
                        if (trg2.ObjectName.Equals(trg.ObjectName))
                        {
                            found = true;

                            // Object was found in the updated schema - check if the two triggers
                            // are equal
                            if (!trg2.Equals(trg))
                            {
                                if (changedTriggers == null)
                                    changedTriggers = new List<SQLiteCreateTriggerStatement>();
                                changedTriggers.Add(trg2);
                            }

                            break;
                        }
                    } // foreach

                    if (!found)
                    {
                        // The updated schema does not contain this trigger so it was removed.
                        if (removedTriggers == null)
                            removedTriggers = new List<SQLiteObjectName>();
                        removedTriggers.Add(trg.ObjectName);
                    } // if
                } // if
            } // foreach

            // Now scan from the direction of the updated schema - see if we can find an trigger
            // that exists in the updated schema but not in the original schema.
            foreach (SQLiteCreateTriggerStatement trg in updTriggers.Values)
            {
                if (SQLiteParser.Utils.Chop(trg.TableName.ToString()).ToLower() == 
                    SQLiteParser.Utils.Chop(tableName).ToLower())
                {
                    // Go look for the same trigger in the original schema. If not found -
                    // add it to the changedTriggers list
                    bool found = false;
                    foreach (SQLiteCreateTriggerStatement trg2 in origTriggers.Values)
                    {
                        if (trg2.ObjectName.Equals(trg.ObjectName))
                        {
                            found = true;
                            break;
                        }
                    } // foreach

                    if (!found)
                    {
                        // The original schema does not contain this index so it was added
                        if (changedTriggers == null)
                            changedTriggers = new List<SQLiteCreateTriggerStatement>();
                        changedTriggers.Add(trg);
                    } // if
                } // if
            } // foreach

            if (changedTriggers == null && removedTriggers == null)
                return false;
            if (changedTriggers != null && removedTriggers != null)
                return true;
            if (changedTriggers == null)
                changedTriggers = new List<SQLiteCreateTriggerStatement>();
            else
                removedTriggers = new List<SQLiteObjectName>();
            return true;            
        }

        /// <summary>
        /// This method compares the original table schema and the updated table schema and checks
        /// if a simple ALTER TABLE can be performed in order to perform the update. If so it returns
        /// the list of columns that need to be added and return TRUE.
        /// </summary>
        /// <param name="orig">The original table schema</param>
        /// <param name="updated">The updated table schema</param>
        /// <param name="added">In case a simple ALTER TABLE can be performed it will be set to the list
        /// of column statements for all columns that need to be added.</param>
        /// <returns>TRUE if we can use the ALTER TABLE command in order to modify the table schema</returns>
        public static bool AlterTableIsPossible(SQLiteDdlStatement orig, SQLiteDdlStatement updated,
            ref List<SQLiteColumnStatement> added)
        {
            SQLiteCreateTableStatement otable = (SQLiteCreateTableStatement)orig;
            SQLiteCreateTableStatement utable = (SQLiteCreateTableStatement)updated;

            // If there is any difference in the table constraints - we can't do a simple ALTER TABLE
            // in order to update the table schema.
            if (otable.Constraints == null && utable.Constraints != null ||
                otable.Constraints != null && utable.Constraints == null)
                return false;
            if (otable.Constraints != null && utable.Constraints != null)
            {
                if (otable.Constraints.Count != utable.Constraints.Count)
                    return false;
                for (int i = 0; i < utable.Constraints.Count; i++)
                {
                    if (!otable.Constraints[i].Equals(utable.Constraints[i]))
                        return false;
                } // for
            } // if

            // If any columns were deleted in the updated table or if a column
            // in the original table does not match the column with the same index
            // in the updated table exactly then we'll have to re-create the table
            // from scratch.
            if (utable.Columns.Count < otable.Columns.Count)
                return false;
            for (int i = 0; i < otable.Columns.Count; i++)
            {
                SQLiteColumnStatement ocol = otable.Columns[i];
                SQLiteColumnStatement ucol = utable.Columns[i];
                if (!ocol.Equals(ucol))
                {
                    // Note: In order to be able to perform ALTER TABLE, all columns that
                    //       exist in the original table must exist in the updated table
                    //       in the same location (index) that they had in the original
                    //       table!
                    return false;
                }
            } // for

            // At this point it is certain that the only kind of difference that can exist
            // between the original table and the updated table is that the updated table
            // contains some added columns.
            if (utable.Columns.Count == otable.Columns.Count)
                throw new ArgumentException("The original table and the updated table are the same!");

            // One last chance to change my mind - ALTER TABLE does not support adding
            // columns where the following conditions are true:
            // 1. The column has a PRIMARY KEY or UNIQUE constraint
            // 2. The column may not have a default value of CURRENT_TIME, CURRENT_DATE or CURRENT_TIMESTAMP.
            // 3. If a NOT NULL constraint is specified, then the column must have a default value other than NULL.
            for (int i = otable.Columns.Count; i < utable.Columns.Count; i++)
            {
                SQLiteColumnStatement col = utable.Columns[i];

                // If the column is NOT NULL without a suitable (not null) default value we can't use
                // ALTER TABLE command to change the table schema.
                if (Utils.IsNotNullColumnWithoutDefault(col))
                    return false;

                if (col.ColumnConstraints != null)
                {
                    foreach (SQLiteColumnConstraint ccon in col.ColumnConstraints)
                    {
                        // Don't allow PRIMARY KEY or UNIQUE constraints
                        if (ccon is SQLitePrimaryKeyColumnConstraint || ccon is SQLiteUniqueColumnConstraint)
                            return false;

                        // Check DEFAULT clause
                        SQLiteDefaultColumnConstraint defcon = ccon as SQLiteDefaultColumnConstraint;
                        if (defcon != null)
                        {
                            // We don't allow default value of CURRENT_TIME, CURRENT_DATE or CURRENT_TIMESTAMP
                            if (defcon.Term != null && defcon.Term.AsTimeFunction.HasValue)
                                return false;
                            if (defcon.Expression != null)
                            {
                                // ALTER TABLE won't accept ADD COLUMN when the column has a DEFAULT clause
                                // with non-constant or constant-but-null value.
                                if (!defcon.Expression.IsConstant(false))
                                    return false;
                            }
                        } // if
                    } // foreach
                } // if
            } // for

            // Prepare the list of columns that need to be added to the table schema.
            added = new List<SQLiteColumnStatement>();
            for (int i = otable.Columns.Count; i < utable.Columns.Count; i++)
                added.Add(utable.Columns[i]);

            return true;
        }

        /// <summary>
        /// Used to check if the specified column has a NOT NULL constraint but no
        /// suitable DEFAULT clause. Such columns may cause problems when re-creating
        /// a table from scratch because we cannot fill them with NULL values if they
        /// don't matching column data in the original table.
        /// </summary>
        /// <param name="col">The column statement to check</param>
        /// <returns>TRUE if the specified column is NOT NULL without a proper DEFAULT
        /// clause.</returns>
        public static bool IsNotNullColumnWithoutDefault(SQLiteColumnStatement col)
        {
            return !col.IsNullable && !col.HasNonNullConstDefault;
        }

        /// <summary>
        /// Creates a new CREATE-TABLE statement from the right table schema object
        /// by changing columns order to match the columns from the left table schema object.
        /// </summary>
        /// <returns>An updated CREATE-TABLE schema object whose columns are in the same order
        /// as the columns of the left table.</returns>
        public static SQLiteCreateTableStatement ReOrderTableColumns(
            SQLiteCreateTableStatement basis, SQLiteCreateTableStatement reordered)
        {
            if (basis.Columns == null || reordered.Columns == null)
                return (SQLiteCreateTableStatement)reordered.Clone();

            List<SQLiteColumnStatement> basisCols = basis.Columns;
            List<SQLiteColumnStatement> reorderedCols = reordered.Columns;

            List<SQLiteColumnStatement> extra = new List<SQLiteColumnStatement>();
            List<SQLiteColumnStatement> clist = new List<SQLiteColumnStatement>();            
            for (int i = 0; i < basisCols.Count; i++)
            {
                SQLiteColumnStatement lcol = basisCols[i];
                foreach (SQLiteColumnStatement rcol in reorderedCols)
                {
                    if (rcol.ObjectName.Equals(lcol.ObjectName))
                    {
                        clist.Add((SQLiteColumnStatement)rcol.Clone());
                        break;
                    }
                } // foreach
            } // for
            foreach (SQLiteColumnStatement rcol in reorderedCols)
            {
                if (!Utils.ColumnListContains(clist, rcol))
                    extra.Add((SQLiteColumnStatement)rcol.Clone());
            } // foreach

            clist.AddRange(extra);

            SQLiteCreateTableStatement res = (SQLiteCreateTableStatement)reordered.Clone();
            res.Columns = clist;
            return res;
        }

        /// <summary>
        /// Format the specified size as a memory unit.
        /// </summary>
        /// <param name="size">The size number to format</param>
        /// <param name="fmt">The format to choose</param>
        /// <returns>The formatted memory size unit (e.g., 100MB)</returns>
        public static string FormatMemSize(long size, MemFormat fmt)
        {
            double num;
            switch (fmt)
            {
                case MemFormat.None:
                    return size.ToString();
                case MemFormat.KB:
                    num = Math.Round((size / 1024.0), 2, MidpointRounding.AwayFromZero);
                    return num.ToString() + "KB";
                case MemFormat.MB:
                    num = Math.Round((size / (1000*1024.0)), 2, MidpointRounding.AwayFromZero);
                    return num.ToString() + "MB";
                case MemFormat.GB:
                    num = Math.Round((size / (1000*1000 * 1024.0)), 2, MidpointRounding.AwayFromZero);
                    return num.ToString() + "GB";
            } // switch

            throw new ArgumentException("unexpected format enumeration");
        }

        /// <summary>
        /// Searches the specified columns list for the specified column
        /// </summary>
        /// <param name="clist">The list of columns to search in</param>
        /// <param name="col">The column to search for</param>
        /// <returns>TRUE if the column was found, FALSE if not.</returns>
        public static bool ColumnListContains(List<SQLiteColumnStatement> clist, SQLiteColumnStatement col)
        {
            foreach (SQLiteColumnStatement c in clist)
            {
                if (c.ObjectName.Equals(col.ObjectName))
                    return true;
            } // foreach
            return false;
        }

        /// <summary>
        /// Checks if the columns list contains a BLOB column.
        /// </summary>
        /// <param name="clist">The list to search</param>
        /// <returns>TRUE if the list contains a BLOB column, FALSE otherwise</returns>
        public static bool ContainsBlobColumn(List<SQLiteColumnStatement> clist)
        {
            foreach (SQLiteColumnStatement col in clist)
            {
                if (Utils.GetDbType(col.ColumnType) == DbType.Binary)
                    return true;
            } // foreach
            return false;
        }

        /// <summary>
        /// Builds a string that contains the names of all specified columns.
        /// This string can than be used e.g., in SELECT statements.
        /// </summary>
        /// <param name="clist">List of column objects</param>
        /// <returns>A string that contains a comma delimited list of all column names</returns>
        public static string BuildColumnsString(List<SQLiteColumnStatement> clist, bool replaceBlobs)
        {
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < clist.Count; i++)
            {
                if (replaceBlobs && Utils.GetDbType(clist[i].ColumnType) == DbType.Binary)
                {
                    // Note: we have to replace a BLOB column name by <NAME> IS NOT NULL expression in order
                    // to avoid a SELECT command from loading the entire BLOB field into memory.
                    sb.Append("(" + clist[i].ObjectName.ToString() + " IS NOT NULL) AS " + clist[i].ObjectName.ToString());
                }
                else
                    sb.Append(clist[i].ObjectName.ToString());
                if (i < clist.Count - 1)
                    sb.Append(",");
            } // for
            return sb.ToString();
        }

        /// <summary>
        /// Builds a list of column equality checks that can be used in WHERE clause
        /// </summary>
        /// <example>[objID] = @objId AND [FirstName] = @FirstName</example>
        /// <param name="clist">The columns list to use</param>
        /// <returns>The string that can be used in a WHERE clause</returns>
        public static string BuildColumnParametersEqualityCheckString(List<SQLiteColumnStatement> clist)
        {
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < clist.Count; i++)
            {
                string pname = Utils.GetColumnParameterName(clist[i].ObjectName.ToString());
                sb.Append(clist[i].ObjectName.ToString()+" = "+pname);
                if (i < clist.Count - 1)
                    sb.Append(" AND ");
            } // for
            return sb.ToString();
        }

        /// <summary>
        /// Build a list of parameter names from the specified columns list.
        /// </summary>
        /// <param name="clist">The columns list</param>
        /// <returns>A string that contains a list of parameter names</returns>
        public static string BuildColumnParametersString(List<SQLiteColumnStatement> clist)
        {
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < clist.Count; i++)
            {
                string pname = Utils.GetColumnParameterName(clist[i].ObjectName.ToString());
                sb.Append(pname);
                if (i < clist.Count - 1)
                    sb.Append(", ");
            } // for
            return sb.ToString();
        }

        /// <summary>
        /// Adds parameters to the specified command based on the columns that are specified
        /// int <paramref name="clist"/> parameter.
        /// </summary>
        /// <param name="cmd">The command we need to add parameters to</param>
        /// <param name="clist">The columns list</param>
        public static void AddCommandColumnParameters(SQLiteCommand cmd, List<SQLiteColumnStatement> clist)
        {
            for (int i = 0; i < clist.Count; i++)
            {
                string pname = Utils.GetColumnParameterName(clist[i].ObjectName.ToString());
                DbType ctype = Utils.GetDbType(clist[i].ColumnType);
                cmd.Parameters.Add(pname, ctype);
            } // for
        }

        /// <summary>
        /// This method is used to check if table comparison is allowed fo the two tables
        /// whose schema objects are provided. In case the comparison is allowed - the method
        /// will provide the list of columns that can be compared.
        /// </summary>
        /// <param name="leftTable">The schema of the left database table</param>
        /// <param name="rightTable">The schema of the right database table</param>
        /// <param name="errmsg">In case the comparison is not allowed - the error message to display</param>
        /// <param name="allowBlobComparison">If TRUE - it assumes that BLOB comparison will take place,
        /// otherwise - it assumes that no BLOB fields will be compared</param>
        /// <returns>
        /// TRUE if table comparison is allowed, FALSE if not.
        /// </returns>
        public static bool IsTableComparisonAllowed(
            SQLiteCreateTableStatement leftTable, SQLiteCreateTableStatement rightTable, 
            out string errmsg, bool allowBlobComparison)
        {
            errmsg = null;

            // Extract the two sets of primary keys for the left table and for the right table
            List<SQLiteColumnStatement> leftpkeys = Utils.GetPrimaryColumns(leftTable);
            List<SQLiteColumnStatement> rightpkeys = Utils.GetPrimaryColumns(rightTable);

            // Make sure that the set of primary keys is identical in both databases.
            if (leftpkeys.Count != rightpkeys.Count)
            {
                errmsg = "The set of primary keys need to be identical in order to perform the comparison";
                return false;
            }
            for (int i = 0; i < leftpkeys.Count; i++)
            {
                SQLiteColumnStatement lpcol = leftpkeys[i];
                SQLiteColumnStatement rpcol = rightpkeys[i];
                if (!Utils.IsColumnTypesMatching(lpcol, rpcol))
                {
                    errmsg = "Primary key column " + lpcol.ObjectName.ToString() + " in the left database table\r\n" +
                        "has a different type than the one in the right database table.";
                    return false;
                }
                if (!lpcol.ObjectName.Equals(rpcol.ObjectName))
                {
                    errmsg = "Primary key columns at index " + i + " have different names.";
                    return false;
                }
            } // for

            // Make sure that there is at least a single common column to both tables
            List<SQLiteColumnStatement> common = Utils.GetCommonColumns(leftTable, rightTable);
            if (common.Count == 0)
            {
                errmsg = "There are no common columns to the two tables.";
                return false;
            }

            return true;
        }

        /// <summary>
        /// Computes the set of common columns to both table schema objects.
        /// </summary>
        /// <param name="fromTable">The table schema object that acts as the basis from which the common columns will be taken</param>
        /// <param name="rightTable">The table schema object that is used for comparison</param>
        /// <returns>The list of common column statements</returns>
        public static List<SQLiteColumnStatement> GetCommonColumns(
            SQLiteCreateTableStatement fromTable,
            SQLiteCreateTableStatement toTable,
            bool ignoreColumnType,
            out List<SQLiteColumnStatement> diffColumns)
        {
            diffColumns = null;
            List<SQLiteColumnStatement> common = new List<SQLiteColumnStatement>();
            for (int i = 0; i < fromTable.Columns.Count; i++)
            {
                SQLiteColumnStatement lcol = fromTable.Columns[i];
                for (int k = 0; k < toTable.Columns.Count; k++)
                {
                    SQLiteColumnStatement rcol = toTable.Columns[k];
                    if (lcol.ObjectName.Equals(rcol.ObjectName))
                    {
                        common.Add(lcol);

                        // This column is common to both the left table and to the right table
                        // so we need to make sure that it has the same type in both tables.
                        if (!ignoreColumnType && !Utils.IsColumnTypesMatching(lcol, rcol))
                        {
                            // Add this column to the list of columns that have different types in
                            // the two databases.
                            if (diffColumns == null)
                                diffColumns = new List<SQLiteColumnStatement>();
                            diffColumns.Add(rcol);
                        } // else
                    } // if
                } // for
            } // for

            return common;
        }

        /// <summary>
        /// Computes the set of common columns to both table schema objects.
        /// </summary>
        /// <param name="leftTable">The left table schema object</param>
        /// <param name="rightTable">The right table schema object</param>
        /// <returns>The list of common column statements</returns>
        public static List<SQLiteColumnStatement> GetCommonColumns(
            SQLiteCreateTableStatement leftTable,
            SQLiteCreateTableStatement rightTable)
        {
            List<SQLiteColumnStatement> dcols = null;
            return GetCommonColumns(leftTable, rightTable, false, out dcols);
        }

        /// <summary>
        /// Selects the appropriate converter method to perform the converstion from the
        /// value object to the required format (specified in the command parameter).
        /// </summary>
        /// <param name="prm">The command parameter</param>
        /// <param name="value">The value to convert</param>
        public static void AssignParameterValue(SQLiteParameter prm, object value)
        {
            Type srctype = value.GetType();
            DbType dsttype = prm.DbType;

            if (_converters.ContainsKey(dsttype) && _converters[dsttype].ContainsKey(srctype))
                prm.Value = _converters[dsttype][srctype](value);
            else
                prm.Value = value;
        }

        /// <summary>
        /// Checks if the given expression is constant or not.
        /// </summary>
        /// <param name="expr">The expression to check</param>
        /// <returns>TRUE if the expression is constant, FALSE otherwise</returns>
        public static bool IsConstantExpression(SQLiteExpression expr)
        {
            SQLiteTermExpression termexp = expr as SQLiteTermExpression;
            if (termexp != null && !termexp.Term.AsTimeFunction.HasValue)
                return true;

            SQLiteBinaryExpression binexp = expr as SQLiteBinaryExpression;
            if (binexp != null)
                return IsConstantExpression(binexp.LeftExpression) && IsConstantExpression(binexp.RightExpression);

            SQLiteUnaryExpression unexp = expr as SQLiteUnaryExpression;
            if (unexp != null)
                return IsConstantExpression(unexp.Expression);

            SQLiteNullExpression nulexp = expr as SQLiteNullExpression;
            if (nulexp != null)
                return true;

            SQLiteCastExpression castexp = expr as SQLiteCastExpression;
            if (castexp != null)
                return IsConstantExpression(castexp.Expression);

            return false;
        }

        /// <summary>
        /// Searches the columns of the specified table statement for a column with the specified name
        /// </summary>
        /// <param name="stmt">The CREATE TABLE statement to search</param>
        /// <param name="colname">The name of the column to search</param>
        /// <returns>The column statement or NULL if not found</returns>
        public static SQLiteColumnStatement GetColumnByName(SQLiteCreateTableStatement stmt, string colname)
        {
            return GetColumnByName(stmt.Columns, colname);
        }

        /// <summary>
        /// Searches the columns of the specified columns list for a column with the specified name
        /// </summary>
        /// <param name="stmt">The CREATE TABLE statement to search</param>
        /// <param name="colname">The name of the column to search</param>
        /// <returns>The column statement or NULL if not found</returns>
        public static SQLiteColumnStatement GetColumnByName(List<SQLiteColumnStatement> clist, string colname)
        {
            foreach (SQLiteColumnStatement col in clist)
            {
                if (SQLiteParser.Utils.Chop(col.ObjectName.ToString().ToLower()) ==
                    SQLiteParser.Utils.Chop(colname.ToLower()))
                    return col;
            } // foreach

            return null;
        }

        /// <summary>
        /// Convenience method for creating temporary DB object names
        /// </summary>
        /// <param name="root">The root name</param>
        /// <returns>A temporary name based on the root name</returns>
        public static string GetTempName(string root)
        {
            if (root == null)
                return "temp_" + (new Random(_seed++).Next());
            else
                return "temp_" + Utils.NormalizeName(root) + "_"+(new Random(_seed++).Next());
        }

        /// <summary>
        /// Create a new SQLite connection to the specified database file
        /// </summary>
        /// <param name="fpath">The path to teh DB file</param>
        /// <param name="writable">TRUE for having read/write connection, FALSE for having
        /// readonly connection</param>
        /// <returns>The SQLite connection object</returns>
        public static SQLiteConnection MakeDbConnection(string fpath, bool writable)
        {
            SQLiteConnectionStringBuilder sb = new SQLiteConnectionStringBuilder();
            sb.DataSource = fpath;
            sb.ReadOnly = !writable;
            return new SQLiteConnection(sb.ConnectionString);
        }

        /// <summary>
        /// Used to strip the input string from any SQL comments that may cause parsing problems.
        /// </summary>
        /// <param name="sql">The input SQL string to strip</param>
        /// <returns>The stripped version of the SQL string without any SQL comments</returns>
        public static string StripComments(string sql)
        {
            sql = _comment.Replace(sql, string.Empty);
            sql = _comment2.Replace(sql, string.Empty);
            return sql;
        }

        /// <summary>
        /// Sometimes, the table names that appear in the database have non-letter/digit characters.
        /// In order to create valid letters that will be used when creating the temporary file name,
        /// we need to normalize these letters to '_'.
        /// </summary>
        /// <param name="name">The original table/db object name</param>
        /// <returns>The normalized version of that name</returns>
        public static string NormalizeName(string name)
        {
            // Remove quotes
            if (name.Length > 1 && name[0] == '[' && name[name.Length - 1] == ']')
                name = name.Substring(1, name.Length - 2);

            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < name.Length; i++)
            {
                if (Char.IsLetterOrDigit(name[i]))
                    sb.Append(name[i]);
                else
                    sb.Append("_");
            } // for
            if (Char.IsDigit(sb[0]))
                sb.Insert(0, "_");
            return sb.ToString();
        }

        /// <summary>
        /// Creates the corresponding column parameter name from a column name.
        /// </summary>
        /// <param name="colname">The name of the column</param>
        /// <returns>The corresponding parameter name</returns>
        public static string GetColumnParameterName(string colname)
        {
            return "@" + Utils.NormalizeName(colname);
        }

        /// <summary>
        /// Extract the list of primary column names from the table statement
        /// </summary>
        /// <param name="table">The table statement object</param>
        /// <returns>The list of primary columns</returns>
        public static List<SQLiteColumnStatement> GetPrimaryColumns(SQLiteCreateTableStatement table)
        {
            List<SQLiteColumnStatement> res = new List<SQLiteColumnStatement>();

            // First - search for the primary column in the columns list part. If found - there can
            // be exactly one column so break as soon as possible from the loop.
            foreach (SQLiteColumnStatement col in table.Columns)
            {
                if (col.ColumnConstraints != null)
                {
                    foreach (SQLiteColumnConstraint ccon in col.ColumnConstraints)
                    {
                        if (ccon is SQLitePrimaryKeyColumnConstraint)
                        {
                            res.Add(col);
                            break;
                        }
                    } // foreach
                    if (res.Count > 0)
                        break;
                } // if
            } // foreach

            // Otherwise - the primary key appears in the table constraints list or not at all.
            if (res.Count == 0)
            {
                if (table.Constraints != null)
                {
                    // Look for the primary key(s) in the table constraints section
                    foreach (SQLiteTableConstraint tcon in table.Constraints)
                    {
                        if (tcon is SQLitePrimaryKeyTableConstraint)
                        {
                            SQLitePrimaryKeyTableConstraint pcon = (SQLitePrimaryKeyTableConstraint)tcon;
                            for (int i = 0; i < pcon.Columns.Count; i++)
                            {
                                SQLiteColumnStatement stmt = FindColumn(table.Columns, pcon.Columns[i].ColumnName);
                                if (stmt == null)
                                    throw new IndexOutOfRangeException("can't find column with name " + pcon.Columns[i].ColumnName);
                                res.Add(stmt);
                            }
                        } // if
                    } // foreach
                } // if
            } // if

            return res;
        }

        /// <summary>
        /// Finds a column with the specified name within the specified columns list.
        /// </summary>
        /// <param name="list">The list to search</param>
        /// <param name="colname">The name of the column to find</param>
        /// <returns>The column object with the specified name</returns>
        public static SQLiteColumnStatement FindColumn(List<SQLiteColumnStatement> list, string colname)
        {
            foreach (SQLiteColumnStatement stmt in list)
            {
                if (SQLiteParser.Utils.Chop(stmt.ObjectName.ToString()).ToLower() == 
                    SQLiteParser.Utils.Chop(colname).ToLower())
                    return stmt;
            } // foreach

            return null;
        }

        /// <summary>
        /// For every column type - return the DbType value that is most appropriate for it.
        /// </summary>
        /// <param name="colType">The column type object</param>
        /// <returns>A DbType enumeration value</returns>
        public static DbType GetDbType(SQLiteColumnType colType)
        {
            string tname = colType.TypeName;
            if (tname == null || tname == string.Empty)
                return DbType.Binary;
            tname = tname.ToLower();

            if (tname.Contains("int"))
                return DbType.Int64;
            if (tname.Contains("float") || tname.Contains("double") || tname.Contains("numeric"))
                return DbType.Double;
            if (tname.Contains("real"))
                return DbType.Single;

            if (tname.Contains("boolean") || tname.Contains("bit"))
                return DbType.Boolean;

            if (colType.TypeName == "datetime" || colType.TypeName == "timestamp")
                return DbType.DateTime;

            if (tname.Contains("char") || tname.Contains("text") || tname.Contains("clob"))
                return DbType.String;

            if (tname.Contains("blob"))
                return DbType.Binary;

            if (tname.Contains("uniqueidentifier") || tname.Contains("guid"))
                return DbType.Guid;

            return DbType.String;
        }

        public static string FormatTableColumnsList(List<SQLiteColumnStatement> list)
        {
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < list.Count; i++)
            {
                SQLiteColumnStatement col = list[i];
                sb.Append(col.ToString());
                if (i < list.Count - 1)
                    sb.Append(",\r\n");
            } // for
            return sb.ToString();
        }

        public static string FormatTableConstraintsList(List<SQLiteTableConstraint> list)
        {
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < list.Count; i++)
            {
                SQLiteTableConstraint tcon = list[i];
                sb.Append(tcon.ToString());
                if (i < list.Count - 1)
                    sb.Append(",\r\n");
            } // for
            return sb.ToString();
        }

        public static DbType GetDbTypeFromClrType(SQLiteColumnStatement column, object updatedValue)
        {
            if (updatedValue == DBNull.Value)
                return Utils.GetDbType(column.ColumnType);
            else
                return Utils.GetDbTypeFromClrType(updatedValue);
        }

        public static DbType GetDbTypeFromClrType(object value)
        {
            if (value is string || value is char)
                return DbType.String;
            if (value is byte || value is sbyte || value is short || value is ushort || value is int ||
                value is uint || value is long || value is ulong)
                return DbType.Int64;
            if (value is double || value is decimal)
                return DbType.Double;
            if (value is float)
                return DbType.Single;
            if (value is byte[])
                return DbType.Binary;
            if (value is bool)
                return DbType.Boolean;
            if (value is DateTime)
                return DbType.DateTime;
            if (value is Guid)
                return DbType.Guid;
            throw new ArgumentException("illegal value type [" + value.GetType().FullName + "]");
        }

        /// <summary>
        /// Check if the specified column acts as an alias to the hidden RowID column.
        /// The (somewhat weird) rules for this can be found at http://www.sqlite.org/lang_createtable.html
        /// </summary>
        /// <param name="table">The table schema object</param>
        /// <param name="name">The column name</param>
        /// <returns>TRUE if the column is acting as a rowid alias, FALSE otherwise</returns>
        public static bool IsColumnActingAsRowIdAlias(SQLiteCreateTableStatement table, string name)
        {
            SQLiteColumnStatement column = Utils.FindColumn(table.Columns, name);
            if (column.ColumnType.TypeName.ToLower() == "integer")
            {
                if (column.ColumnConstraints != null)
                {
                    foreach (SQLiteColumnConstraint cons in column.ColumnConstraints)
                    {
                        SQLitePrimaryKeyColumnConstraint pcon = cons as SQLitePrimaryKeyColumnConstraint;
                        if (pcon != null)
                        {
                            // See http://www.sqlite.org/lang_createtable.html (bottom of the page) for more details
                            if (pcon.SortOrder == SQLiteSortOrder.Ascending || pcon.SortOrder == SQLiteSortOrder.None)
                                return true;
                            else
                                return false;
                        } // if
                    } // foreach
                }

                if (table.Constraints != null)
                {
                    foreach (SQLiteTableConstraint tcon in table.Constraints)
                    {
                        SQLitePrimaryKeyTableConstraint ptcon = tcon as SQLitePrimaryKeyTableConstraint;
                        if (ptcon != null)
                        {
                            if (ptcon.Columns.Count == 1)
                            {
                                if (SQLiteParser.Utils.Chop(ptcon.Columns[0].ColumnName).ToLower() ==
                                    SQLiteParser.Utils.Chop(name).ToLower())
                                {
                                    return true;
                                }
                            }
                            break;
                        } // if
                    } // foreach
                } // if
            }

            return false;
        }

        #region Private Methods
        private static void PrepareConverters()
        {
            ValueConverter emptyString = delegate { return string.Empty; };
            ValueConverter toString = delegate(object value) { return value.ToString(); };

            Dictionary<Type, ValueConverter> strcon = new Dictionary<Type, ValueConverter>();
            strcon.Add(typeof(byte[]), delegate(object value) { return Encoding.ASCII.GetString((byte[])value); });
            strcon.Add(typeof(bool), delegate(object value) { return (bool)value ? "1" : "0"; });

            _converters.Add(DbType.AnsiString, strcon);
            _converters.Add(DbType.AnsiStringFixedLength, strcon);
            _converters.Add(DbType.String, strcon);
            _converters.Add(DbType.StringFixedLength, strcon);

            Dictionary<Type, ValueConverter> dtcon = new Dictionary<Type, ValueConverter>();
            dtcon.Add(typeof(long), delegate(object value)
            {
                return new DateTime(1, 1, 1);
            });
            dtcon.Add(typeof(Guid), delegate(object value)
            {
                return new DateTime(1, 1, 1);
            });
            dtcon.Add(typeof(byte[]), delegate(object value)
            {
                return new DateTime(1, 1, 1);
            });
            dtcon.Add(typeof(double), delegate(object value)
            {
                return new DateTime(1, 1, 1);
            });
            dtcon.Add(typeof(float), delegate(object value)
            {
                return new DateTime(1, 1, 1);
            });
            dtcon.Add(typeof(bool), delegate(object value)
            {
                return new DateTime(1, DateTimeKind.Utc);
            });
            dtcon.Add(typeof(string), delegate(object value)
            {
                DateTime dt;
                string str = (string)value;
                if (DateTime.TryParse(str, out dt))
                    return dt;
                else
                {
                    // YYYY-DD-MM [HH:MM:SS]
                    Match m = _datetime.Match(str);
                    if (m.Success)
                    {
                        int year = int.Parse(m.Groups[1].Value);
                        int day = int.Parse(m.Groups[3].Value);
                        int month = int.Parse(m.Groups[5].Value);

                        // Did we match the time string?
                        if (m.Groups[6].Success)
                        {
                            int hour = int.Parse(m.Groups[7].Value);
                            int min = int.Parse(m.Groups[8].Value);
                            int sec = int.Parse(m.Groups[9].Value);

                            try
                            {
                                return new DateTime(year, month, day, hour, min, sec);
                            }
                            catch (Exception ex)
                            {
                                // Ignore format exceptions
                            }
                        }
                        else
                        {
                            try
                            {
                                return new DateTime(year, month, day);
                            }
                            catch (Exception ex)
                            {
                                // Ignore format exceptions
                            } // catch
                        } // else
                    }
                    else
                    {
                        m = _timerx.Match(str);
                        if (m.Success)
                        {
                            int hour = int.Parse(m.Groups[1].Value);
                            int min = int.Parse(m.Groups[2].Value);
                            int sec = int.Parse(m.Groups[3].Value);

                            try
                            {
                                return new DateTime(2000, 1, 1, hour, min, sec);
                            }
                            catch (Exception ex)
                            {
                                // Ignore format exceptions
                            }
                        } // if
                    } // else

                    return new DateTime(1, 1, 1);
                }
            });
            _converters.Add(DbType.DateTime, dtcon);

            Dictionary<Type, ValueConverter> bolcon = new Dictionary<Type, ValueConverter>();
            bolcon.Add(typeof(string), delegate(object value) { return (string)value == "0" ? false : true; });
            bolcon.Add(typeof(byte[]), delegate(object value) { return false; });
            bolcon.Add(typeof(Guid), delegate(object value) { if (((Guid)value).Equals(Guid.Empty)) return false; else return true; });
            bolcon.Add(typeof(DateTime), delegate(object value)
            {
                return false;
            });
            bolcon.Add(typeof(long), delegate(object value)
            {
                if (((long)value) == 0)
                    return false;
                return true;
            });
            _converters.Add(DbType.Boolean, bolcon);

            Dictionary<Type, ValueConverter> numcon = new Dictionary<Type, ValueConverter>();
            numcon.Add(typeof(byte[]), delegate(object value) { return 0; });
            numcon.Add(typeof(string), delegate(object value) 
            {
                long lval = 0;
                long.TryParse((string)value, out lval);
                return lval; 
            });
            numcon.Add(typeof(DateTime), delegate(object value)
            {
                DateTime dt = (DateTime)value;
                return dt.Ticks;
            });
            numcon.Add(typeof(Guid), delegate(object value)
            {
                byte[] arr = ((Guid)value).ToByteArray();
                return BitConverter.ToInt64(arr, 0);
            });
            _converters.Add(DbType.Int16, numcon);
            _converters.Add(DbType.Int32, numcon);
            _converters.Add(DbType.Int64, numcon);
            _converters.Add(DbType.SByte, numcon);
            _converters.Add(DbType.Byte, numcon);
            _converters.Add(DbType.UInt16, numcon);
            _converters.Add(DbType.UInt32, numcon);
            _converters.Add(DbType.UInt64, numcon);

            Dictionary<Type, ValueConverter> singcon = new Dictionary<Type, ValueConverter>();
            singcon.Add(typeof(string), delegate(object value)
            {
                float fval = 0;
                float.TryParse((string)value, out fval);
                return fval;
            });
            singcon.Add(typeof(DateTime), delegate(object value)
            {
                return 0;
            });
            singcon.Add(typeof(Guid), delegate(object value)
            {
                return 0;
            });
            singcon.Add(typeof(byte[]), delegate(object value)
            {
                return 0;
            });
            _converters.Add(DbType.Single, singcon);

            Dictionary<Type, ValueConverter> doubcon = new Dictionary<Type, ValueConverter>();
            doubcon.Add(typeof(string), delegate(object value)
            {
                double dval = 0;
                double.TryParse((string)value, out dval);
                return dval;
            });
            doubcon.Add(typeof(float), delegate(object value)
            {
                double dval = double.Parse(((float)value).ToString());
                return dval;
            });
            doubcon.Add(typeof(DateTime), delegate(object value)
            {
                return 0;
            });
            doubcon.Add(typeof(Guid), delegate(object value)
            {
                return 0;
            });
            doubcon.Add(typeof(byte[]), delegate(object value)
            {
                return 0;
            });
            _converters.Add(DbType.Double, doubcon);

            Dictionary<Type, ValueConverter> blobcon = new Dictionary<Type, ValueConverter>();
            blobcon.Add(typeof(string), delegate(object value)
            {
                string tmp = (string)value;
                byte[] res = Encoding.ASCII.GetBytes(tmp);
                return res;
            });
            blobcon.Add(typeof(bool), delegate(object value)
            {
                bool tmp = (bool)value;
                if (tmp)
                    return new byte[] { (byte)1 };
                else
                    return new byte[] { (byte)0 };
            });
            blobcon.Add(typeof(Guid), delegate(object value)
            {
                Guid tmp = (Guid)value;
                return tmp.ToByteArray();
            });
            blobcon.Add(typeof(double), delegate(object value)
            {
                return new byte[0];
            });
            blobcon.Add(typeof(DateTime), delegate(object value)
            {
                return new byte[0];
            });
            blobcon.Add(typeof(float), delegate (object value)
            {
                return new byte[0];
            });
            blobcon.Add(typeof(long), delegate(object value)
            {
                return new byte[0];
            });
            _converters.Add(DbType.Binary, blobcon);

            Dictionary<Type, ValueConverter> guidcon = new Dictionary<Type, ValueConverter>();
            guidcon.Add(typeof(DateTime), delegate(object value)
            {
                return Guid.Empty;
            });
            guidcon.Add(typeof(long), delegate(object value)
            {
                long lval = (long)value;
                byte[] tmp = BitConverter.GetBytes(lval);
                byte[] arr = new byte[16];
                for (int i = 0; i < tmp.Length; i++)
                    arr[i] = tmp[i];
                return new Guid(arr);
            });
            guidcon.Add(typeof(string), delegate(object value)
            {
                Guid res = Guid.Empty;
                IsGuid((string)value, out res);
                return res;
            });
            guidcon.Add(typeof(double), delegate(object value)
                {
                    return Guid.Empty;
                });
            guidcon.Add(typeof(float), delegate(object value)
                {
                    return Guid.Empty;
                });
            guidcon.Add(typeof(byte[]), delegate(object value)
            {
                byte[] tmp = (byte[])value;
                byte[] arr = new byte[16];
                for (int i = 0; i < 16 && i < tmp.Length; i++)
                    arr[i] = tmp[i];
                return new Guid(arr);
            });
            guidcon.Add(typeof(bool), delegate(object value)
            {
                return Guid.Empty;
            });
            _converters.Add(DbType.Guid, guidcon);
        }
        #endregion

        #region Private Types
        private delegate object ValueConverter(object value);
        #endregion

        private static Regex _datetime = new Regex(@"(\d\d\d\d)(\-|\s)(\d\d)(\-|\s)(\d\d)(\s+(\d\d)\:(\d\d)\:(\d\d))?");
        private static Regex _timerx = new Regex(@"(\d\d)\:(\d\d)\:(\d\d)");
        private static Regex isGuid = new Regex(@"^(\{){0,1}[0-9a-fA-F]{8}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{12}(\}){0,1}$", RegexOptions.Compiled);
        private static Regex _comment = new Regex(@"(/\*([^*]|[\r\n]|(\*+([^*/]|[\r\n])))*\*+/)");
        private static Regex _comment2 = new Regex(@"(\-\-[^\n]*)\n");
        private static Dictionary<DbType, Dictionary<Type, ValueConverter>> _converters = new Dictionary<DbType, Dictionary<Type, ValueConverter>>();
        private static int _seed = 1;
    }

    /// <summary>
    /// Used to format memory size numbers
    /// </summary>
    public enum MemFormat
    {
        None = 0,

        KB = 1,

        MB = 2,

        GB = 3,
    }
}

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