Files
Common-VS2019/SacsStandardDbas/Dbas/Base/SacsDatabase.cs
T

285 lines
13 KiB
C#

using Microsoft.Win32;
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.Utils;
using Sarand.Net.Standard.Dbas.Data;
using System;
using System.Collections.Generic;
using System.Data.Common;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
namespace Sarand.Net.Standard.Dbas.Base
{
//=======================================================================
public abstract class SacsDatabase : SacsStateItem, ISacsDatabase
{
//-------------------------------------------------------------------
// Data members.
//-------------------------------------------------------------------
// Construction.
protected SacsDatabase(DbmsType dbms, String databaseName, String connectionString)
{
DbmsType = dbms;
DatabaseName = databaseName;
ConnectionString = connectionString;
}
//-------------------------------------------------------------------
// Database properties.
public String ConnectionString { get; }
public DbmsType DbmsType { get; }
public String DatabaseName { get; }
public SelectLockMode SelectLockMode { get; set; } = SelectLockMode.Default;
public bool DisplayErrors { get; set; } = true;
public abstract void OnConnectionChanged();
public abstract DbConnection CreateConnection(bool forceOdbc = false);
public abstract DbDataAdapter CreateDataAdapter();
// Profile properties.
public static String ProfileSection_DbUtilities { get; } = "DbUtilities";
public static String ProfileValue_DbExportFile { get; } = "DbExportFile";
public static String ProfileValue_DbExportDsn { get; } = "DbExportDsn";
public static String ProfileValue_DbExportFmt { get; } = "DbExportFmt";
public static String ProfileValue_SecondArchivePath { get; } = "SecondaryArchivePath";
public static String ProfileValue_SecondArchive { get; } = "SecondaryArchive";
public static String ProfileValue_CompareSourceDbDsn { get; } = "CompareDbSourceDsn";
public static String ProfileValue_CompareTargetDbDsn { get; } = "CompareDbTargetDsn";
public static String ProfileValue_DlgPos_DbView { get; } = "DlgPos:DbView";
public static String ProfileValue_DlgPos_DbCompare { get; } = "DlgPos:DbCompare";
//-------------------------------------------------------------------
// I d e n t i t y G e n e r a t i o n
//-------------------------------------------------------------------
protected String NextIdTable { get; set; } = "NextId";
protected String NextIdColumn_Object { get; set; } = "Object";
protected String NextIdColumn_NextId { get; set; } = "NextId";
public abstract SysId GetNextId(String objectName, DbUpdateInfo dbUpdateInfo, bool databaseTable = true, String idColumn = "Id");
public abstract int GetNextIdFromTable(string objectName, string idColumn = "Id");
public abstract bool SetMinNextId(String objectName, SysId id, DbUpdateInfo dbUpdateInfo, bool databaseTable = true, String idColumn = "Id", bool force = false);
//-------------------------------------------------------------------
// O D B C D r i v e r s
//-------------------------------------------------------------------
[DllImport("odbccp32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
private static extern bool SQLGetInstalledDriversW(char[] lpszBuf, ushort cbufMax, out ushort pcbBufOut);
/// <summary>
/// Gets the ODBC driver names from the SQLGetInstalledDrivers function.
/// <para>Ref. https://stackoverflow.com/questions/6457973/odbc-driver-list-from-net</para>
/// </summary>
/// <returns>List of ODBC driver names, if successfull.</returns>
public static List<String> GetOdbcDriverNames()
{
try
{
// ToDo Switch to SQLDrivers to get name and attributes.
// Ref. https://social.msdn.microsoft.com/Forums/en-US/e98d3beb-35d5-4899-b277-0f7c568c6215/list-of-odbc-drivers-installed?forum=visualfoxprogeneral.
// P/Invoke to SQLGetInstalledDriversW.
// NB Only returns names and not attributes.
char[] driverNamesBuffer = new char[ushort.MaxValue];
ushort size;
bool succeeded = SQLGetInstalledDriversW(driverNamesBuffer, ushort.MaxValue, out size);
if (succeeded == true)
{
char[] driverNames = new char[size - 1];
Array.Copy(driverNamesBuffer, driverNames, size - 1);
return new List<String>(new string(driverNames).Split('\0')).OrderBy(item => item).ToList();
}
}
catch (Exception) { }
try
{
#pragma warning disable CA1416
// Alternatively, use the registry.
// Validate platform compatibility
using (RegistryKey localMachineHive = Registry.LocalMachine)
using (RegistryKey odbcDriversKey = localMachineHive.OpenSubKey(@"SOFTWARE\ODBC\ODBCINST.INI\ODBC Drivers"))
{
if (odbcDriversKey != null)
return new List<String>(odbcDriversKey.GetValueNames()).OrderBy(item => item).ToList();
}
// Validate platform compatibility
// 64-BitConverter machines.
using (RegistryKey localMachineHive = Registry.LocalMachine)
using (RegistryKey odbcDriversKey = localMachineHive.OpenSubKey(@"SOFTWARE\Wow6432Node\ODBC\ODBCINST.INI\ODBC Drivers"))
{
if (odbcDriversKey != null)
return new List<String>(odbcDriversKey.GetValueNames()).OrderBy(item => item).ToList();
}
#pragma warning restore CA1416
}
catch (Exception) { }
return new List<String>();
}
//-------------------------------------------------------------------
// O D B C D a t a S o u r c e s
//-------------------------------------------------------------------
[DllImport("odbc32.dll")]
public static extern int SQLDataSources(int EnvHandle, int Direction, StringBuilder ServerName, int ServerNameBufferLenIn, ref int ServerNameBufferLenOut, StringBuilder Driver, int DriverBufferLenIn, ref int DriverBufferLenOut);
[DllImport("odbc32.dll")]
public static extern int SQLAllocEnv(ref int EnvHandle);
public static List<SaDbDfnOdbcDataSource> GetOdbcDatasources()
{
List<SaDbDfnOdbcDataSource> dataSources = new List<SaDbDfnOdbcDataSource>();
int envHandle = 0;
const int SQL_FETCH_NEXT = 1;
const int SQL_FETCH_FIRST_SYSTEM = 32;
if (SQLAllocEnv(ref envHandle) != -1)
{
int ret;
StringBuilder serverName = new StringBuilder(1024);
StringBuilder driverName = new StringBuilder(1024);
int snLen = 0;
int driverLen = 0;
ret = SQLDataSources(envHandle, SQL_FETCH_FIRST_SYSTEM, serverName, serverName.Capacity, ref snLen, driverName, driverName.Capacity, ref driverLen);
while (ret == 0)
{
SaDbDfnOdbcDataSource dataSource = new SaDbDfnOdbcDataSource(serverName.ToString(), driverName.ToString());
dataSources.Add(dataSource);
ret = SQLDataSources(envHandle, SQL_FETCH_NEXT, serverName, serverName.Capacity, ref snLen, driverName, driverName.Capacity, ref driverLen);
}
}
return dataSources.OrderBy(item => item.Name).ToList();
}
//-------------------------------------------------------------------
// Q u e r y D a t a b a s e
//-------------------------------------------------------------------
public String GetSelectQueryPrefix()
{
// Query prefix.
switch (SelectLockMode)
{
case SelectLockMode.ReadCommitted:
if (DbmsType == DbmsType.MsSql)
{
// Recommended for SQL2005+ to allow committed reads the same visibility as uncommitted reads:
// -- Enable row versioning for the default Read Committed isolation level
// ALTER DATABASE DevSchoolOneLMS SET READ_COMMITTED_SNAPSHOT ON;
// -- Enable row versioning as a separate Snapshot isolation level
// ALTER DATABASE DevSchoolOneLMS SET ALLOW_SNAPSHOT_ISOLATION ON;
// Enabling row versioning allows the default READ COMMITTED isolation level to see a consistant
// version of the row without resorting to SHARE locks that block UPDATE statements.
return "Set Transaction Isolation Level Read Committed; ";
}
break;
case SelectLockMode.ReadUncommitted:
if (DbmsType == DbmsType.MsSql)
return "Set Transaction Isolation Level Read Uncommitted; ";
break;
case SelectLockMode.RepeatableRead:
if (DbmsType == DbmsType.MsSql)
return "Set Transaction Isolation Level Repeatable Read; ";
break;
case SelectLockMode.Serializable:
if (DbmsType == DbmsType.MsSql)
return "Set Transaction Isolation Level Serializable; ";
break;
}
return "";
}
public abstract DbResultSet ExecuteQuery(String query);
public abstract int ExecuteNonQuery(String query);
public abstract int ExecuteInsert(String tableName, List<StringPair> columnNameValues);
public abstract int ExecuteUpdate(String tableName, List<StringPair> columnNameValues, String filter);
public abstract int ExecuteDelete(String tableName, String filter);
/// <summary>
/// Add value pair - used to build value lists for insert and update.
/// <para>Caters for delimiting text values and uses 'NULL' for empty text values.</para>
/// </summary>
public static void AddValuePair(List<StringPair> columnNameValues, String columnName, String value, bool delimitString = false)
{
if (delimitString)
{
if (!StringUtil.IsEmpty(value))
value = "'" + value.Duplicate('\'') + "'";
else value = "NULL";
}
columnNameValues.Add(columnName, value);
}
/// <summary>Add SysId value pair - used to build value lists for insert and update.</summary>
public static void AddValuePair(List<StringPair> columnNameValues, String columnName, SysId value)
{
columnNameValues.Add(columnName, SysId.ToInt(value).ToString());
}
/// <summary>Add numeric value pair - used to build value lists for insert and update.</summary>
public static void AddValuePair(List<StringPair> columnNameValues, String columnName, Int32? value)
{
columnNameValues.Add(columnName, value is null ? "NULL" : $"{value}");
}
/// <summary>Add numeric value pair - used to build value lists for insert and update.</summary>
public static void AddValuePair(List<StringPair> columnNameValues, String columnName, Int64? value)
{
columnNameValues.Add(columnName, value is null ? "NULL" : $"{value}");
}
/// <summary>Add decimal value pair - used to build value lists for insert and update.</summary>
public static void AddValuePair(List<StringPair> columnNameValues, String columnName, decimal? value)
{
columnNameValues.Add(columnName, value is null ? "NULL" : $"{value}");
}
/// <summary>Add date/time value pair - used to build value lists for insert and update.</summary>
public static void AddValuePair(List<StringPair> columnNameValues, String columnName, DateTime? value)
{
AddValuePair(columnNameValues, columnName, value is null ? "NULL" : DateUtil.Format(value, "yyyy/MM/dd HH:mm:ss"), true);
}
// Specific queries.
public abstract long GetRowCount(String tableName, String filter);
public abstract DataValue GetRowValue(String tableName, String columnName, String filter);
public abstract DataValue GetRowValue(String tableName, String columnName, String filter, DbGetValueType getValueType);
//-------------------------------------------------------------------
// D a t a b a s e S c h e m a
//-------------------------------------------------------------------
public abstract HashSet<String> GetTableNames(bool refresh = false);
public abstract List<DbDfnTable> GetTableDfns(bool refresh = false);
public abstract DbDfnTable GetTableDfn(String name, bool refresh = false);
public bool TableExists(String tableName)
{
if (StringUtil.IsEmpty(tableName)) return false;
return GetTableNames().Contains(tableName);
}
public bool ColumnExists(String tableName, String columnName)
{
if (StringUtil.IsEmpty(tableName) || StringUtil.IsEmpty(columnName)) return false;
DbDfnTable table = GetTableDfn(tableName);
if (table != null)
return table.HasColumnByName(columnName);
return false;
}
public bool IndexExists(String tableName, String indexName)
{
if (StringUtil.IsEmpty(tableName) || StringUtil.IsEmpty(indexName)) return false;
DbDfnTable table = GetTableDfn(tableName);
if (table != null)
return table.HasIndexByName(indexName);
return false;
}
//-------------------------------------------------------------------
// Database info - schema summary.
public IDatabaseInfo CreateDbInfo()
{
return new DbDataItem.DatabaseInfo(this);
}
//-------------------------------------------------------------------
// Logging.
public static SaMessageLevel LoggingLevel { get; set; } = SaMessageLevel.None;
public static String LoggingPrefix { get; set; } = " ~~~ ";
protected static void Log(String fn, String message, SaMessageLevel level = SaMessageLevel.Debug)
{
if ((int)level > (int)LoggingLevel) return;
SaLogger.Log((fn + "\n\t" + message).Trim(), level);
}
//-------------------------------------------------------------------
}
//=======================================================================
}