using Microsoft.AspNetCore.Mvc; using Sarand.Net.Standard.Base.App; using Sarand.Net.Standard.Base.Bus; using Sarand.Net.Standard.Base.Core; using Sarand.Net.Standard.Base.Dbas; using Sarand.Net.Standard.Base.Helpers; using Sarand.Net.Standard.Base.Procs; using Sarand.Net.Standard.Base.Utils; using Sarand.Net.Standard.Dbas.Data; using Sarand.Net.Standard.Dbas.Proc; using Saxonite.Net.Core.Web.Admin.Models; using Saxonite.Net.Standard.Base.App; using Saxonite.Net.Standard.Base.Bus; using Saxonite.Net.Standard.Dbas.App; using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Threading.Tasks; namespace Saxonite.Net.Core.Web.Admin.Controllers.System { //======================================================================= // System - Ajax - Database controller //======================================================================= partial class SystemController { //------------------------------------------------------------------- // Database data. private class DatabaseTableData { public String SchemaName { get; set; } public String TableName { get; set; } public String QualifiedName { get; set; } public int RowCount { get; set; } public int ColumnCount { get; set; } public int IndexCount { get; set; } } //------------------------------------------------------------------- // Get Database Table List ajax request. //------------------------------------------------------------------- private class AjaxResponse_DatabaseTableList : AjaxResponse_Data { // Construction. public AjaxResponse_DatabaseTableList(SaxModel_Admin model) : base(model) { } // Table list. public List Tables { get; } = new List(); } //------------------------------------------------------------------- [HttpPost] [Route("System/Ajax/GetDatabaseTables")] public IActionResult Ajax_GetDatabaseTables(Model_System model) { String fn = GetType().Name + ".Ajax_GetDatabaseTables"; String messageTitle = Localiser["system.caption.database"]; AjaxResponse_DatabaseTableList responseData = new AjaxResponse_DatabaseTableList(model); // Validate session. if (!ValidUserSession) return GetAjaxResult_InvalidSession(responseData); // Record disposable items as they are created so all can be disposed when needed. using (DisposableCollection disposableItems = new DisposableCollection()) { // Database connection. Sax.Database databaseInstance = EnumUtil.ParseEnum(StringUtil.EmptyOnNull(model.DatabaseFilter.Instance), Sax.Database.Core); ISacsDatabase db = App.GetDatabase(SessionContext, databaseInstance); // Database table row counts. Dictionary tableRowCounts = new Dictionary(StringComparer.OrdinalIgnoreCase); String query = "SELECT QUOTENAME(SCHEMA_NAME(sOBJ.schema_id)) + '.' + QUOTENAME(sOBJ.name) As [TableName], SUM(sPTN.Rows) As [RowCount] " + "FROM sys.objects AS sOBJ " + "INNER JOIN sys.partitions AS sPTN ON sOBJ.object_id = sPTN.object_id " + "WHERE sOBJ.type = 'U' AND sOBJ.is_ms_shipped = 0x0 AND index_id< 2 /*Index Id: 0:Heap,1:Clustered*/ " + "GROUP BY sOBJ.schema_id, sOBJ.name"; using (DbResultSet dbResultSet = DbResultSet_DataReader.ExecuteQuery(db, query)) { while (dbResultSet != null && dbResultSet.NextRecord()) { tableRowCounts.Add(dbResultSet.GetRecordValues().GetValue("TableName").ToString(), (int)dbResultSet.GetRecordValues().GetValue("RowCount").AsLong()); } } // Database tables. List tableDfns = db.GetTableDfns(); foreach (DbDfnTable tableDfn in tableDfns.OrderBy(item => item.Name)) { // Filter by table name. if (!StringUtil.IsEmpty(model.DatabaseFilter.Search) && !tableDfn.Name.ContainsCI(model.DatabaseFilter.Search)) continue; DatabaseTableData tableData = new DatabaseTableData(); tableData.SchemaName = tableDfn.Schema; tableData.TableName = tableDfn.Name; tableData.QualifiedName = tableDfn.QualifiedName; tableData.ColumnCount = tableDfn.ColumnCount; tableData.IndexCount = tableDfn.IndexCount; // Row count. tableData.RowCount = tableRowCounts.GetValueOrDefault(tableDfn.QuotedQualifiedName, 0); responseData.Tables.Add(tableData); } } // Complete response. responseData.Success = true; return Json(responseData); } //------------------------------------------------------------------- // Validate Database Schema ajax request. //------------------------------------------------------------------- private class DbSchemaItemResponseData { public int Id { get; set; } public String TableName { get; set; } public String ColumnName { get; set; } public int ColumnPosition { get; set; } public String IndexName { get; set; } public String Comment { get; set; } } private class AjaxResponse_ValidateDatabaseSchema : AjaxResponse_Data { // Construction. public AjaxResponse_ValidateDatabaseSchema(SaxModel_Admin model) : base(model) { } // Database instance. public String Instance { get; set; } // Schema validation list. public List Items { get; } = new List(); } //------------------------------------------------------------------- [HttpPost] [Route("System/Ajax/ValidateDatabaseSchema")] public IActionResult Ajax_ValidateDatabaseSchema(Model_System model) { String fn = GetType().Name + ".Ajax_ValidateDatabaseSchema"; String messageTitle = Localiser["system.caption.database"]; AjaxResponse_ValidateDatabaseSchema responseData = new AjaxResponse_ValidateDatabaseSchema(model); // Validate session. if (!ValidUserSession) return GetAjaxResult_InvalidSession(responseData); // Record disposable items as they are created so all can be disposed when needed. using (DisposableCollection disposableItems = new DisposableCollection()) { // Database instance. Sax.Database databaseInstance = EnumUtil.ParseEnum(StringUtil.EmptyOnNull(model.DatabaseFilter.Instance), Sax.Database.Unknown); if (databaseInstance <= 0) return Json(responseData.SetMessage(messageTitle, "Database instance must be specified")); responseData.Instance = databaseInstance.ToString(); // Database connection. ISacsDatabase db = App.GetDatabase(SessionContext, databaseInstance); if (db == null) return Json(responseData.SetMessage(messageTitle, "Unrecognised database instance specified: " + model.DatabaseFilter.Instance)); // Retrieve XML database schema definition. var assembly = typeof(SaxSystem_Dbas).GetTypeInfo().Assembly; String xmlSchema = SacsResourceHelper.GetManifestResourceAsText(assembly, "", "SaxDbDfn_" + databaseInstance.ToString() + ".xml"); if (StringUtil.IsEmpty(xmlSchema)) return Json(responseData.SetMessage(messageTitle, "Failed to extract XML schema definition.")); // Extract schema from XML definition. SaDbProcess_ConvertDbSchema.Config cfgSchemaConvert = new SaDbProcess_ConvertDbSchema.Config(); SaDbProcess_ConvertDbSchema procSchemaConvert = new SaDbProcess_ConvertDbSchema(cfgSchemaConvert); List tables = procSchemaConvert.ConvertXmlToTables(xmlSchema); if (procSchemaConvert.HasErrorMessage()) return Json(responseData.SetMessage(messageTitle, "Failed to convert XML schema: " + procSchemaConvert.GetErrorMessage())); SaDbProcess_CompareDatabaseSchemas.Config cfgSchemaCompare = new SaDbProcess_CompareDatabaseSchemas.Config(tables, db); cfgSchemaCompare.HideIdenticalObjects = true; cfgSchemaCompare.IgnorePosition = true; SaDbProcess_CompareDatabaseSchemas procSchemaCompare = new SaDbProcess_CompareDatabaseSchemas(cfgSchemaCompare); procSchemaCompare.OnSchemaComparisonItemAdded += OnSchemaComparisonItemAdded; procSchemaCompare.RunProcess(); List schemaComparisonItems = procSchemaCompare.GetSchemaComparisonItems(); foreach (SaDbProcess_CompareDatabaseSchemas.SchemaComparisonItem schemaComparisonItem in schemaComparisonItems) { DbSchemaItemResponseData item = new DbSchemaItemResponseData() { Id = schemaComparisonItem.Id, TableName = schemaComparisonItem.TableName, ColumnName = schemaComparisonItem.ColumnName, ColumnPosition = schemaComparisonItem.ColumnPosition, IndexName = schemaComparisonItem.IndexName, Comment = schemaComparisonItem.Comment }; responseData.Items.Add(item); } } // Complete response. responseData.Success = true; return Json(responseData); } // Processing events. public void OnSchemaComparisonItemAdded(object sender, SaDbProcess_CompareDatabaseSchemas.SchemaComparisonItemAddedEventArgs e) { SaDbProcess_CompareDatabaseSchemas proc = sender as SaDbProcess_CompareDatabaseSchemas; } //------------------------------------------------------------------- // Database Table ajax request. //------------------------------------------------------------------- private class AjaxResponseData_Table { public AjaxResponseData_Table(DbDfnTable tableDfn) { Schema = tableDfn.Schema; Name = tableDfn.Name; if (!StringUtil.IsEmpty(tableDfn.Remarks)) Remarks = tableDfn.Remarks; foreach (DbDfnColumn columnDfn in tableDfn.Columns) Columns.Add(new AjaxResponseData_Column(columnDfn)); foreach (DbDfnIndex indexDfn in tableDfn.Indices) Indices.Add(new AjaxResponseData_Index(indexDfn)); } public String Schema { get; } public String Name { get; } public String QualifiedName { get { return (!StringUtil.IsEmpty(Schema) ? Schema + "." : "") + Name; } } public String QuotedQualifiedName { get { return (!StringUtil.IsEmpty(Schema) ? "[" + Schema + "]." : "") + "[" + Name + "]"; } } public String Remarks { get; set; } = String.Empty; public List Properties { get; } = new List(); public List Columns { get; } = new List(); public List Indices { get; } = new List(); } private class AjaxResponseData_Column { public int Pos { get; } public String Name { get; } public String Format { get; } public String DataTypeName { get; } public short? DataTypeNative { get; } public String DataTypeNameNative { get; } public bool? IsIdentity { get; } public long? Size { get; } public long? BufferLen { get; } public short? DecimalDigits { get; } public bool? Nullable { get; } public bool? IsNullable { get; } public String Remarks { get; } public AjaxResponseData_Column(DbDfnColumn columnDfn) { Pos = columnDfn.Pos; Name = columnDfn.Name; Format = columnDfn.GetFormat(); if (!StringUtil.IsEmpty(columnDfn.DataTypeName)) DataTypeName = columnDfn.DataTypeName; if (columnDfn.DataTypeNative > 0) DataTypeNative = columnDfn.DataTypeNative; if (!StringUtil.IsEmpty(columnDfn.DataTypeNameNative)) DataTypeNameNative = columnDfn.DataTypeNameNative; if (columnDfn.IsIdentity) IsIdentity = columnDfn.IsIdentity; if (columnDfn.Size > 0) Size = columnDfn.Size; if (columnDfn.BufferLen > 0) BufferLen = columnDfn.BufferLen; if (columnDfn.DecimalDigits > 0) DecimalDigits = columnDfn.DecimalDigits; if (columnDfn.Nullable == DbColumnNullable.Nullable) Nullable = true; if (columnDfn.IsNullable) IsNullable = columnDfn.IsNullable; if (!StringUtil.IsEmpty(columnDfn.Remarks)) Remarks = columnDfn.Remarks; } } private class AjaxResponseData_Index { public String Name { get; } public int? IndexType { get; } public bool? Unique { get; } public bool? Primary { get; } public bool? Clustered { get; } public bool? Nullable { get; } public int? Pages { get; } public int? UniqueValues { get; } public List Columns { get; } = new List(); public AjaxResponseData_Index(DbDfnIndex indexDfn) { Name = indexDfn.Name; if (indexDfn.IndexType > 0) IndexType = indexDfn.IndexType; if (indexDfn.Unique) Unique = indexDfn.Unique; if (indexDfn.Primary) Primary = indexDfn.Primary; if (indexDfn.Clustered) Clustered = indexDfn.Clustered; if (indexDfn.Nullable) Nullable = indexDfn.Nullable; if (indexDfn.Pages > 0) Pages = indexDfn.Pages; if (indexDfn.UniqueValues > 0) UniqueValues = indexDfn.UniqueValues; foreach (DbDfnIndexColumn indexColumnDfn in indexDfn.Columns) Columns.Add(new AjaxResponseData_IndexColumn(indexColumnDfn)); } } private class AjaxResponseData_IndexColumn { public short Seq { get; } public String Name { get; } public bool? Desc { get; } public AjaxResponseData_IndexColumn(DbDfnIndexColumn indexColumnDfn) { Seq = indexColumnDfn.Seq; Name = indexColumnDfn.Name; if (!indexColumnDfn.Ascend) Desc = true; } } private class AjaxResponse_DatabaseTable : AjaxResponse_Data { // Construction. public AjaxResponse_DatabaseTable(SaxModel_Admin model) : base(model) { } // Database table. public String PopupTitle { get; set; } public AjaxResponseData_Table Table { get; set; } } //------------------------------------------------------------------- [HttpPost] [Route("System/Ajax/GetDatabaseTable")] public IActionResult Ajax_GetDatabaseTable(Model_System model) { String fn = GetType().Name + ".Ajax_GetDatabaseTable"; String messageTitle = Localiser["system.caption.database"]; AjaxResponse_DatabaseTable responseData = new AjaxResponse_DatabaseTable(model); // Validate session. if (!ValidUserSession) return GetAjaxResult_InvalidSession(responseData); // Record disposable items as they are created so all can be disposed when needed. using (DisposableCollection disposableItems = new DisposableCollection()) { // Parameters. if (StringUtil.IsEmpty(model.DatabaseTable.Name)) return Json(responseData.SetMessage(messageTitle, "Table name must be supplied.")); // Database instance. Sax.Database databaseInstance = EnumUtil.ParseEnum(StringUtil.EmptyOnNull(model.DatabaseTable.Instance), Sax.Database.Unknown); if (databaseInstance <= 0) return Json(responseData.SetMessage(messageTitle, "Database instance must be specified")); // Database connection. ISacsDatabase db = App.GetDatabase(SessionContext, databaseInstance); // Table definition. DbDfnTable tableDfn = db.GetTableDfn(model.DatabaseTable.Name, true); if (tableDfn == null) return Json(responseData.SetMessage(messageTitle, "Invalid Table name supplied.")); // Populate table response. responseData.Table = new AjaxResponseData_Table(tableDfn); responseData.PopupTitle = Localiser["label.table"] + ": " + tableDfn.Name; // Database sizing. String query = "Exec sp_SpaceUsed [" + tableDfn.QualifiedName + "];"; using (DbResultSet dbResultSet = DbResultSet_DataReader.ExecuteQuery(db, query)) { if (dbResultSet != null && dbResultSet.NextRecord()) { // Row count. long rows = StringUtil.ToLong(dbResultSet.GetRecordValues().GetValue("rows").ToString()); if (rows > 0) responseData.Table.Properties.Add(new AjaxResponse_Data.ItemLabelValue() { Label = "Rows", Value = rows.ToString() }); // Reserved space. String reserved = dbResultSet.GetRecordValues().GetValue("reserved").AsString(); if (!StringUtil.IsEmpty(reserved)) responseData.Table.Properties.Add(new AjaxResponse_Data.ItemLabelValue() { Label = "Reserved", Value = reserved }); // Data space. String data = dbResultSet.GetRecordValues().GetValue("data").AsString(); if (!StringUtil.IsEmpty(data)) responseData.Table.Properties.Add(new AjaxResponse_Data.ItemLabelValue() { Label = "Data", Value = data }); // Index space. String index = dbResultSet.GetRecordValues().GetValue("index_size").AsString(); if (!StringUtil.IsEmpty(index)) responseData.Table.Properties.Add(new AjaxResponse_Data.ItemLabelValue() { Label = "Indices", Value = index }); // Unused space. String unused = dbResultSet.GetRecordValues().GetValue("unused").AsString(); if (!StringUtil.IsEmpty(unused)) responseData.Table.Properties.Add(new AjaxResponse_Data.ItemLabelValue() { Label = "Unused", Value = unused }); } } } // Complete response. responseData.Success = true; return Json(responseData); } //------------------------------------------------------------------- } //======================================================================= }