Files
Common-VS2019/SacsStandardDbas/Dbas/Base/SacsDatabaseConsole.cs
T
2021-07-10 14:23:52 +01:00

334 lines
13 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using Sarand.Net.Standard.Base.Core;
using Sarand.Net.Standard.Base.Bus;
using Sarand.Net.Standard.Base.Dbas;
using Sarand.Net.Standard.Dbas.Data;
using Sarand.Net.Standard.Base.App;
using Sarand.Net.Standard.Base.Utils;
namespace Sarand.Net.Standard.Dbas.Base
{
//=======================================================================
public class SacsDatabaseConsole : SaErrorStateItem
{
//-------------------------------------------------------------------
// Database configuration
public class OdbcConfig
{
public DbmsType dbms = DbmsType.General;
public String connectionString; // Raw connection string.
public String location, user, pword; // Application properties.
/// <summary>Database name populated when connection string is generated if not done previously.</summary>
public String DatabaseName { get; set; }
}
//-------------------------------------------------------------------
// Data members.
//-------------------------------------------------------------------
// Construction.
//-------------------------------------------------------------------
// Utilities.
private void LogSql(String sql)
{
SaLogger.Debug(GetType().Name + " SQL: " + sql);
}
//-------------------------------------------------------------------
// Database configuration.
//-------------------------------------------------------------------
public static DbmsType GetDbmsType(OdbcConfig config)
{
if (config.dbms == DbmsType.MsSql || config.location.Left(6).EqualsCI("MsSql:"))
return DbmsType.MsSql;
return config.dbms;
}
public static String GenerateConnectionString(OdbcConfig config, bool dbmsSpecificConnectionString = false)
{
// Construct connection string.
String connectionString = String.Empty;
DbmsType dbms = GetDbmsType(config);
// SQL connection.
if (dbms == DbmsType.MsSql)
{
// MsSql:Server:Port\Instance.Database
dbms = DbmsType.MsSql;
String sqlDriver = OdbcUtil.GetOdbcSqlDriverName();
String sqlLocation = config.location.Left(6).EqualsCI("MsSql:") ? config.location.Substring(6) : config.location;
String sqlServer = "."; /* Assume local host */
String sqlInstance = "";
String sqlPort = "";
String sqlDatabase = sqlLocation;
if (!String.IsNullOrEmpty(sqlLocation) && sqlLocation.IndexOf('\\') >= 0)
{
// Server=[protocol:]Server;
String[] locationSplit = sqlLocation.Split('\\');
sqlServer = locationSplit[0];
sqlDatabase = locationSplit[1];
}
if (!String.IsNullOrEmpty(sqlServer) && sqlServer.IndexOf(':') > 0)
{
// Server=[protocol:]Server[,port];
String[] serverSplit = sqlServer.Split(':');
sqlServer = serverSplit[0];
sqlPort = serverSplit[1];
}
if (!String.IsNullOrEmpty(sqlDatabase) && sqlDatabase.IndexOf('.') > 0)
{
// Server=[protocol:]Server[\instancename];
String[] databaseSplit = sqlDatabase.Split('.');
sqlInstance = databaseSplit[0];
sqlDatabase = databaseSplit[1];
}
if (!StringUtil.IsEmpty(sqlPort)) sqlServer += "," + sqlPort;
if (!StringUtil.IsEmpty(sqlInstance)) sqlServer += "\\" + sqlInstance;
if (!dbmsSpecificConnectionString)
{
// ODBC connection string for SQL Server.
// Ref. https://docs.microsoft.com/en-us/sql/relational-databases/native-client/applications/using-connection-string-keywords-with-sql-server-native-client
// Address Network address of the server running an instance of SQL Server.
// Driver=<OdbcDriver>;
// Description=<DSN Caption>;
// UID=<User>;
// APP=<ApplicationCaption>;
// WsId=<WorkstationId>;
// Server=[protocol:]Server[,port][\instancename];
// Database=<Default database>;
// Trusted_Connection=<Yes|No>;
// UseProcForPrepare=2
connectionString = "Driver={" + sqlDriver + "};";
connectionString += "Server=" + sqlServer + ";";
connectionString += "Database=" + sqlDatabase + ";";
if (!config.user.IsEmpty())
{
connectionString += "UID=" + config.user + ";";
connectionString += "PWD=" + config.pword + ";";
}
connectionString += (String)"Trusted_Connection=" + (config.user.IsEmpty() ? "Yes" : "No") + ";";
// Multiple Active Result Sets.
connectionString += "MARS_Connection=yes;";
//if (iQueryLogTime > 0)
// connectionString += "QueryLogTime="+String(iQueryLogTime)+";";
//if (iCommandTimeout > 0)
// connectionString += "CommandTimeout=" + String(iCommandTimeout) + ";";
}
else
{
// SQL Connection string.
connectionString = "";
connectionString += "Server=" + sqlServer + ";";
connectionString += "Database=" + sqlDatabase + ";";
if (!config.user.IsEmpty())
{
connectionString += "User Id=" + config.user + ";";
connectionString += "Password=" + config.pword + ";";
}
connectionString += (String)"Trusted_Connection=" + (config.user.IsEmpty() ? "Yes" : "No") + ";";
}
// Populate configuration values if blank.
if (config.dbms <= 0)
config.dbms = dbms;
if (StringUtil.IsEmpty(config.DatabaseName))
config.DatabaseName = sqlDatabase;
SaLogger.Info("DatabaseConsole.GenerateConnectionString Driver: " + sqlDriver + ". Server: " + sqlServer + ". Database: " + sqlDatabase + ". Connection: " + connectionString);
}
return connectionString;
}
//-------------------------------------------------------------------
// O p e n O D B C D a t a b a s e
//-------------------------------------------------------------------
public static SacsDatabase OpenOdbcDatabase(OdbcConfig config)
{
// Populate configuration.
config.dbms = GetDbmsType(config);
// Connection strings.
String connectionString = config.connectionString;
if (StringUtil.IsEmpty(connectionString))
connectionString = GenerateConnectionString(config);
String sqlConnectionString = GenerateConnectionString(config, true);
// Open database.
DbGlobalDbms.Type = config.dbms;
SacsDatabaseOdbc database = new SacsDatabaseOdbc(config.dbms, config.DatabaseName, connectionString);
database.SelectLockMode = SelectLockMode.ReadUncommitted;
database.SqlConnectionString = sqlConnectionString;
return database;
}
//-------------------------------------------------------------------
// D a t a b a s e I n d i c e s
//-------------------------------------------------------------------
// Database Index stats.
public class IndexStats
{
public int PrimaryIndicesCreated { get; set; } = 0;
public int IndicesCreated { get; set; } = 0;
public int IndicesDropped { get; set; } = 0;
public List<String> Stats
{
get
{
List<String> stats = new List<string>();
if (PrimaryIndicesCreated > 0)
stats.Add("Primary indices created: " + PrimaryIndicesCreated.ToString());
if (IndicesCreated > 0)
stats.Add("Indices created: " + IndicesCreated.ToString());
if (IndicesDropped > 0)
stats.Add("Indices dropped: " + IndicesDropped.ToString());
return stats;
}
}
}
//-------------------------------------------------------------------
// Drop index.
protected bool DropDbIndex(ISacsDatabase database, IndexStats indexStats, String tableName, String indexName)
{
if (database == null || HasErrorState()) return false;
if (!database.TableExists(tableName)) return true;
DbDfnTable dfnTbl = database.GetTableDfn(tableName, true);
if (!dfnTbl.HasIndexByName(indexName)) // Should also check the columns and their sequence in the index.
{
// Drop index:
// MS Access: DROP INDEX index_name ON table_name
// MS SQL Server: DROP INDEX table_name.index_name
// DB2/Oracle: DROP INDEX index_name
// MySQL: ALTER TABLE table_name DROP INDEX index_name
String query = "DROP INDEX [" + indexName + "] ON [" + tableName + "]";
LogSql(query);
if (database.DbmsType == DbmsType.MsSql)
query = "DROP INDEX [" + tableName + "].[" + indexName + "]";
if (!StringUtil.IsEmpty(query))
{
database.ExecuteNonQuery(query);
if (!database.HasErrorState())
indexStats.IndicesDropped++;
else
{
#if DEBUG
SaLogger.Debug("DropDbIndex Table: " + tableName + "; Index: " + indexName + "; Exception: " + database.ErrorStateMessage);
#endif
SetErrorState("Failed to drop index " + indexName + " on " + tableName + " - " + database.ErrorStateMessage);
}
}
}
else
{
// Assume this is the Primary Key:
String query = "ALTER TABLE [" + tableName + "] DROP CONSTRAINT [" + indexName + "]";
LogSql(query);
//if (database.DbmsType() == eSaDbmsMsSql)
// sQry = "DROP INDEX ["+tableName+"].["+indexName+"]";
if (!StringUtil.IsEmpty(query))
{
database.ExecuteNonQuery(query);
if (!database.HasErrorState())
indexStats.IndicesDropped++;
else
{
#if DEBUG
SaLogger.Debug("DropDbIndex Table: " + tableName + "; Index: " + indexName + "; Exception: " + database.ErrorStateMessage);
#endif
SetErrorState("Failed to drop primary key " + indexName + " on " + tableName + " - " + database.ErrorStateMessage);
}
}
}
return !HasErrorState();
}
//-------------------------------------------------------------------
// Create primary index.
protected bool CreatePrimaryDbIndex(ISacsDatabase database, IndexStats indexStats, String tableName, String columnsCsv, bool dropIndicesIfNotExist = true)
{
if (database == null || HasErrorState()) return false;
if (!database.TableExists(tableName)) return true;
List<String> lColumn = StringListUtil.Split(columnsCsv, ",");
DbDfnTable dfnTbl = database.GetTableDfn(tableName, true);
String indexName = "PK_" + tableName;
if (!dfnTbl.HasIndexByName(indexName))
{
if (dropIndicesIfNotExist)
{
foreach (DbDfnIndex indexDfn in dfnTbl.Indices)
{
DropDbIndex(database, indexStats, tableName, indexDfn.Name);
}
}
if (!HasErrorState())
{
// Examples:
// MS Access: ALTER TABLE <table> ADD PRIMARY KEY (<ColumnList>)
// 1. ALTER TABLE Customer ADD PRIMARY KEY (SID);
// 2. ALTER TABLE [Sales].[Individual]
// ADD CONSTRAINT [PK_Individual_CustomerID]
// PRIMARY KEY CLUSTERED
// ([CustomerID] ASC)
String query = "ALTER TABLE [" + tableName + "] ADD CONSTRAINT [" + indexName + "] PRIMARY KEY (" + lColumn.Concat(",", "[", "]") + ")";
if (database.DbmsType == DbmsType.MsSql)
query = "ALTER TABLE [" + tableName + "] ADD CONSTRAINT [" + indexName + "] PRIMARY KEY CLUSTERED (" + lColumn.Concat(",", "[", "]") + ")";
LogSql(query);
if (!StringUtil.IsEmpty(query))
{
database.ExecuteNonQuery(query);
if (!database.HasErrorState())
indexStats.PrimaryIndicesCreated++;
else
{
#if DEBUG
SaLogger.Debug("CreatePrimaryDbIndex Table: %" + tableName + "; Index: " + indexName + "; Exception: " + database.ErrorStateMessage);
#endif
SetErrorState("Failed to create primary index " + indexName + " on " + tableName + " - " + database.ErrorStateMessage);
}
}
}
}
return !HasErrorState();
}
//-------------------------------------------------------------------
// Create database index.
protected bool CreateDbIndex(ISacsDatabase database, IndexStats indexStats, String tableName, String indexName, String columnsCsv, bool unique = false)
{
if (database == null || HasErrorState()) return false;
if (!database.TableExists(tableName)) return true;
List<String> columns = StringListUtil.Split(columnsCsv, ",");
DbDfnTable dfnTbl = database.GetTableDfn(tableName, true);
// Construct the index name:
String indexNamePrefix = "IDX_" + tableName + "_";
if (!indexName.StartsWith(indexNamePrefix)) indexName = indexNamePrefix + indexName;
if (!dfnTbl.HasIndexByName(indexName)) // Should also check the columns and their sequence in the index.
{
String query = "CREATE " + (unique ? "UNIQUE " : "") + "INDEX ";
query += "[" + indexName + "] ON [" + tableName + "] (" + columns.Concat(",", "[", "]") + ")";
LogSql(query);
if (!StringUtil.IsEmpty(query))
{
database.ExecuteNonQuery(query);
if (!database.HasErrorState())
indexStats.IndicesCreated++;
else
{
#if DEBUG
SaLogger.Debug("CreateDbIndex Table: " + tableName + "; Index: " + indexName + "; Exception: " + database.ErrorStateMessage);
#endif
SetErrorState("Failed to create index " + indexName + " on " + tableName + " - " + database.ErrorStateMessage);
}
}
}
return !HasErrorState();
}
//-------------------------------------------------------------------
}
//=======================================================================
}