#region License // Copyright 2005-2019 Paul Kohler (https://github.com/paulkohler/minisqlquery). All rights reserved. // This source code is made available under the terms of the GNU Lesser General Public License v3.0 // https://github.com/paulkohler/minisqlquery/blob/master/LICENSE #endregion using System; using System.Collections.Generic; using System.Data; using System.Data.Common; namespace MiniSqlQuery.Core.DbModel { /// SQL Compact Edition schema service. /// Made possible with contributions form ExportSQLCE project. /// http://exportsqlce.codeplex.com/ /// http://erikej.blogspot.com/ public class SqlCeSchemaService : GenericSchemaService { /// The ma x_ binar y_ colum n_ size. internal static readonly int MAX_BINARY_COLUMN_SIZE = 8000; /// The ma x_ imag e_ colum n_ size. internal static readonly int MAX_IMAGE_COLUMN_SIZE = 1073741823; /// The ma x_ ncha r_ colum n_ size. internal static readonly int MAX_NCHAR_COLUMN_SIZE = 4000; /// The ma x_ ntex t_ colum n_ size. internal static readonly int MAX_NTEXT_COLUMN_SIZE = 536870911; /// The _connection. private string _connection; /// The get db object model. /// The connection. /// public override DbModelInstance GetDbObjectModel(string connection) { _connection = connection; DbModelInstance model = new DbModelInstance(); DbProviderFactory factory = DbProviderFactories.GetFactory(ProviderName); using (DbConnection dbConn = factory.CreateConnection()) { dbConn.ConnectionString = connection; dbConn.Open(); QueryTableNames(dbConn, model); Dictionary dbTypes = GetDbTypes(dbConn); model.Types = dbTypes; model.ProviderName = ProviderName; model.ConnectionString = _connection; // build all table info foreach (DbModelTable table in model.Tables) { DataTable schemaTableKeyInfo = GetTableKeyInfo(dbConn, null, table.Name); GetColumnsForTable(table, schemaTableKeyInfo, dbTypes); } // build FK relationships foreach (DbModelTable table in model.Tables) { GetForeignKeyReferencesForTable(dbConn, table); ProcessForeignKeyReferencesForTable(dbConn, table); } return model; } } /// Gets the db types for the SQL CE provider. /// The connection (not required). /// public override Dictionary GetDbTypes(DbConnection connection) { Dictionary dbTypes = new Dictionary(); string dataTypesSql = "SELECT * FROM information_schema.provider_types"; using (var cmd = connection.CreateCommand()) { cmd.CommandText = dataTypesSql; cmd.CommandType = CommandType.Text; using (var reader = cmd.ExecuteReader()) { while (reader.Read()) { string typeName = (string)reader["TYPE_NAME"]; int columnSize = Convert.ToInt32(reader["COLUMN_SIZE"]); DbModelType dbType = new DbModelType(typeName, columnSize); dbType.CreateParameters = Convert.ToString(reader["CREATE_PARAMS"]); dbType.LiteralPrefix = Convert.ToString(reader["LITERAL_PREFIX"]); dbType.LiteralSuffix = Convert.ToString(reader["LITERAL_SUFFIX"]); dbType.ProviderDbType = Convert.ToString(reader["DATA_TYPE"]); FixCreateFormat(dbType); FixMaxLengths(dbType); AssignSystemTypes(dbType); dbTypes.Add(typeName, dbType); } } } return dbTypes; } /// The get data type name for column. /// The db table. /// The schema table key info. /// The column row. /// The get data type name for column. protected override string GetDataTypeNameForColumn(DbModelTable dbTable, DataTable schemaTableKeyInfo, DataRow columnRow) { return SafeGetString(columnRow, "ProviderType"); } /// The get foreign key references for table. /// The db conn. /// The db table. protected override void GetForeignKeyReferencesForTable(DbConnection dbConn, DbModelTable dbTable) { ForeignKeyInformationAvailable = true; try { using (var cmd = dbConn.CreateCommand()) { cmd.CommandText = string.Format( @"SELECT KCU1.TABLE_NAME AS FK_TABLE_NAME, KCU1.CONSTRAINT_NAME AS FK_CONSTRAINT_NAME, KCU1.COLUMN_NAME AS FK_COLUMN_NAME, KCU2.TABLE_NAME AS UQ_TABLE_NAME, KCU2.CONSTRAINT_NAME AS UQ_CONSTRAINT_NAME, KCU2.COLUMN_NAME AS UQ_COLUMN_NAME, RC.UPDATE_RULE, RC.DELETE_RULE, KCU2.ORDINAL_POSITION AS UQ_ORDINAL_POSITION, KCU1.ORDINAL_POSITION AS FK_ORDINAL_POSITION FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS RC JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE KCU1 ON KCU1.CONSTRAINT_NAME = RC.CONSTRAINT_NAME JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE KCU2 ON KCU2.CONSTRAINT_NAME = RC.UNIQUE_CONSTRAINT_NAME AND KCU2.ORDINAL_POSITION = KCU1.ORDINAL_POSITION AND KCU2.TABLE_NAME = RC.UNIQUE_CONSTRAINT_TABLE_NAME WHERE KCU1.TABLE_NAME = '{0}' ORDER BY FK_TABLE_NAME, FK_CONSTRAINT_NAME, FK_ORDINAL_POSITION ", dbTable.Name); cmd.CommandType = CommandType.Text; using (var dr = cmd.ExecuteReader()) { while (dr.Read()) { dbTable.Constraints.Add(new DbModelConstraint { ConstraintTableName = dr.GetString(0), ConstraintName = dr.GetString(1), ColumnName = dr.GetString(2), UniqueConstraintTableName = dr.GetString(3), UniqueConstraintName = dr.GetString(4), UniqueColumnName = dr.GetString(5), UpdateRule = dr.GetString(6), DeleteRule = dr.GetString(7) }); } } } } catch (DbException) { ForeignKeyInformationAvailable = false; } } /// The process foreign key references for table. /// The db conn. /// The db table. protected override void ProcessForeignKeyReferencesForTable(DbConnection dbConn, DbModelTable dbTable) { // todo - check GetGroupForeingKeys foreach (DbModelConstraint constraint in dbTable.Constraints) { var column = dbTable.Columns.Find(c => c.Name == constraint.ColumnName); var refTable = dbTable.ParentDb.FindTable( Utility.RenderSafeSchemaObjectName(constraint.UniqueConstraintTableSchema, constraint.UniqueConstraintTableName)); var refColumn = refTable.Columns.Find(c => c.Name == constraint.UniqueColumnName); DbModelForeignKeyReference fk = new DbModelForeignKeyReference(column, refTable, refColumn); fk.UpdateRule = constraint.UpdateRule; fk.DeleteRule = constraint.DeleteRule; column.ForeignKeyReference = fk; } } /// The assign system types. /// The db type. private void AssignSystemTypes(DbModelType dbType) { switch (dbType.Name.ToLower()) { case "smallint": dbType.SystemType = typeof(byte); break; case "int": dbType.SystemType = typeof(int); break; case "tinyint": dbType.SystemType = typeof(byte); break; case "bigint": dbType.SystemType = typeof(long); break; case "float": dbType.SystemType = typeof(double); // yes, float is double ;-) break; case "numeric": case "money": case "real": dbType.SystemType = typeof(decimal); break; case "bit": dbType.SystemType = typeof(bool); break; case "uniqueidentifier": dbType.SystemType = typeof(Guid); break; case "nvarchar": case "nchar": case "ntext": dbType.SystemType = typeof(string); break; case "datetime": dbType.SystemType = typeof(DateTime); break; case "varbinary": case "binary": case "image": case "rowversion": dbType.SystemType = typeof(byte[]); break; } } /// The fix create format. /// The db type. private void FixCreateFormat(DbModelType dbType) { switch (dbType.Name.ToLower()) { case "nchar": case "nvarchar": case "binary": case "varbinary": dbType.CreateFormat = dbType.Name.ToLower() + "({0})"; break; case "decimal": case "numeric": dbType.CreateFormat = dbType.Name.ToLower() + "({0}, {1})"; break; } } /// The fix max lengths. /// The db type. private void FixMaxLengths(DbModelType dbType) { switch (dbType.Name.ToLower()) { case "nchar": case "nvarchar": dbType.Length = MAX_NCHAR_COLUMN_SIZE; break; case "ntext": dbType.Length = MAX_NTEXT_COLUMN_SIZE; break; case "binary": case "varbinary": dbType.Length = MAX_BINARY_COLUMN_SIZE; break; case "image": dbType.Length = MAX_IMAGE_COLUMN_SIZE; break; } } /// The query table names. /// The db conn. /// The model. private void QueryTableNames(DbConnection dbConn, DbModelInstance model) { using (var cmd = dbConn.CreateCommand()) { cmd.CommandText = "SELECT table_name FROM information_schema.tables WHERE TABLE_TYPE = N'TABLE'"; cmd.CommandType = CommandType.Text; using (var reader = cmd.ExecuteReader()) { while (reader.Read()) { DbModelTable table = new DbModelTable(); table.Name = (string)reader["table_name"]; model.Add(table); } } } } } }