// Spludlow Software // Copyright © Samuel P. Ludlow 2020 All Rights Reserved // Distributed under the terms of the GNU General Public License version 3 // Distributed WITHOUT ANY WARRANTY; without implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE // https://www.spludlow.co.uk/LICENCE.TXT // The Spludlow logo is a registered trademark of Samuel P. Ludlow and may not be used without permission // v1.14 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.IO; using System.Data; using System.Data.Common; using System.Data.Odbc; using System.ServiceModel; namespace Spludlow.Data { public class DALODBC : Spludlow.Data.IDAL { private OdbcConnection Connection = null; private OdbcTransaction Transaction = null; private string CacheKeyPrefix; private string QuoteOpen = "\""; private string QuoteClose = "\""; private Dictionary ConvertDataTypes; private object Lock = new object(); public DALODBC() { } public DALODBC(string connectionString) { this.Initialize(connectionString); } public void Initialize(string connectionString) { lock (this.Lock) { if (this.Connection != null) return; if (connectionString == null || connectionString.Length == 0) return; if (connectionString.StartsWith("@") == true) connectionString = Spludlow.Config.ConnectionString(connectionString.Substring(1)); // use hash of connection string !!!!!!!!!!! this.Connection = new OdbcConnection(connectionString); this.CacheKeyPrefix = "DALODBC-Schemas-" + Guid.NewGuid().ToString(); this.ConvertDataTypes = new Dictionary(); string[] convertLookups = new string[] { "Numeric Decimal", }; foreach (string convertLookup in convertLookups) { string[] words = Spludlow.Text.Split(convertLookup, '\t', true, false); this.ConvertDataTypes.Add(words[0], words[1]); } } } public static Spludlow.Data.IDAL MakeClient(string serviceAppKey) { string address = Spludlow.Config.RemotingAddress(serviceAppKey); NetTcpBinding binding = new NetTcpBinding(SecurityMode.None); binding.MaxReceivedMessageSize = 1 * 1024 * 1024 * 1024; // Large datasets, What about uploads(bulk insterts)? binding.SendTimeout = TimeSpan.FromMinutes(15); // Large datasets binding.ReceiveTimeout = TimeSpan.FromMinutes(15); EndpointAddress endpointAddress = new EndpointAddress(address); Spludlow.Data.IDAL channel = ChannelFactory.CreateChannel(binding, endpointAddress); return channel; } // // Open Close // private void Open() { this.Connection.Open(); //if (this.ChangedDatabase != null) //{ // try // { // string database = this.ChangedDatabase; // if (database == "") // database = ""; // !!!!!!!!!!!!!!! // this.Connection.ChangeDatabase(database); // } // catch // { // this.Connection.Close(); // throw; // } //} } private void Close() { this.Connection.Close(); } public string DataSource() { this.Open(); try { return this.Connection.DataSource; } finally { this.Close(); } } public void CommandTimeout(int minutes) { } // // Transactions // public void Begin() { if (this.Transaction != null) throw new ApplicationException("DAL ODBC: Begin Transaction already present."); this.Open(); try { this.Transaction = this.Connection.BeginTransaction(); } catch { this.Close(); throw; } } public void Commit() { if (this.Transaction == null) throw new ApplicationException("DAL ODBC: Commit Transaction not present."); try { this.Transaction.Commit(); this.Transaction = null; } finally { this.Close(); } } public void Rollback() { if (this.Transaction == null) throw new ApplicationException("DAL ODBC: Rollback Transaction not present."); try { this.Transaction.Rollback(); this.Transaction = null; } finally { this.Close(); } } // // Schema // public DataSet Schema(string tableName) { return this.Schema(new string[] { tableName }); } public DataSet Schema(string[] tableNames) { DataSet fullSchema = this.Schema(); DataSet partSchema = new DataSet(); foreach (string tableName in tableNames) { foreach (string suffix in new string[] { "_Columns", "_Keys" }) partSchema.Tables.Add(fullSchema.Tables[tableName + suffix].Copy()); } return partSchema; } public DataSet Schema() { string key = this.CacheKeyPrefix; DataSet schema = (DataSet)Spludlow.Caching.Get(key); if (schema != null) return schema; schema = new DataSet(); DataSet schemaReader = this.SchemaReader(); foreach (string tableName in this.TableList()) { this.SchemaColumns(schema, tableName, schemaReader); this.SchemaKeys(schema, tableName, schemaReader); } Spludlow.Caching.Set(key, schema); return schema; } private void SchemaColumns(DataSet schema, string tableName, DataSet schemaReader) { SysTables.SchemaColumnsDataTable schemaTable = Spludlow.Data.Schemas.NewSchemaColumnsTable(tableName); foreach (DataRow readerRow in schemaReader.Tables[tableName].Rows) { SysTables.SchemaColumnsRow schemaRow = schemaTable.NewSchemaColumnsRow(); OdbcType dbType = (OdbcType)Spludlow.Data.Casting.CastInt32(readerRow, "ProviderType"); string typeName = dbType.ToString(); if (this.ConvertDataTypes.ContainsKey(typeName) == true) typeName = this.ConvertDataTypes[typeName]; schemaRow.ColumnName = (string)readerRow["ColumnName"]; schemaRow.Ordinal = Spludlow.Data.Casting.CastInt32(readerRow, "ColumnOrdinal"); schemaRow.DataTypeId = Spludlow.Data.Schemas.NativeToCommon(typeName, "OdbcType"); schemaRow.MaxLength = Spludlow.Data.Casting.CastInt32(readerRow, "ColumnSize"); schemaRow.Precision = Spludlow.Data.Casting.CastInt32(readerRow, "NumericPrecision"); schemaRow.Scale = Spludlow.Data.Casting.CastInt32(readerRow, "NumericScale"); schemaRow.PrimaryKey = Spludlow.Data.Casting.CastBoolean(readerRow, "IsKey"); schemaRow.AllowDBNull = Spludlow.Data.Casting.CastBoolean(readerRow, "AllowDBNull"); schemaRow.AutoIncrement = Spludlow.Data.Casting.CastBoolean(readerRow, "IsAutoIncrement"); schemaRow.Longest = 0; schemaTable.Rows.Add(schemaRow); } Spludlow.Data.Schemas.TidySchemaTable(schemaTable); schema.Tables.Add(schemaTable); } private void SchemaKeys(DataSet schema, string tableName, DataSet schemaReader) { SysTables.SchemaKeysDataTable schemaTable = Spludlow.Data.Schemas.NewSchemaKeysTable(tableName); List keyColumns = new List(); foreach (DataRow readerRow in schemaReader.Tables[tableName].Rows) { if (Spludlow.Data.Casting.CastBoolean(readerRow, "IsKey") == true) keyColumns.Add((string)readerRow["ColumnName"]); } for (int ordinal = 0; ordinal < keyColumns.Count; ++ordinal) schemaTable.Rows.Add(new object[] { "PK_" + tableName, "P", ordinal, false, keyColumns[ordinal], null, null }); schema.Tables.Add(schemaTable); } public DataSet SchemaConnection() { //string databaseName = this.CurrentDatabase(); //if (databaseName == null) // throw new ApplicationException("DAL OleDb; To get the Standard schema you must have a current database"); DataSet schema; if (this.Transaction == null) this.Open(); try { schema = Spludlow.Data.DALCommon.SchemaConnection(this.Connection); //, "Schema", "public"); } finally { if (this.Transaction == null) this.Close(); } //DataTable table; //table = schema.Tables["Columns"]; //table.PrimaryKey = new DataColumn[] { table.Columns["TABLE_NAME"], table.Columns["COLUMN_NAME"] }; //table = schema.Tables["PrimaryKeys"]; //table.PrimaryKey = new DataColumn[] { table.Columns["CONSTRAINT_NAME"] }; //table = schema.Tables["UniqueKeys"]; //table.PrimaryKey = new DataColumn[] { table.Columns["INDEX_NAME"] }; return schema; } public DataSet SchemaReader() { string[] tableNames = this.TableList(); DataSet schema; if (this.Transaction == null) this.Open(); try { schema = Spludlow.Data.DALCommon.SchemaReader(this.Connection, this.Transaction, tableNames, this.QuoteOpen, this.QuoteClose); } finally { if (this.Transaction == null) this.Close(); } return schema; } public DataSet SchemaNative() { return new DataSet(); } public DataSet SchemaANSI() { return new DataSet(); } public void SchemaRefresh() { string key = this.CacheKeyPrefix; Spludlow.Caching.Remove(key); } // // Tables // public string[] TableList() { DataSet schemaStandard = this.SchemaConnection(); // Maybe just get it direct ? if (schemaStandard.Tables.Contains("Tables") == false) throw new ApplicationException("DAL ODBC, TableList; The 'Tables' table is not in the Standard Schema."); DataTable table = schemaStandard.Tables["Tables"]; if (table.Columns.Contains("TABLE_NAME") == false) throw new ApplicationException("DAL ODBC, TableList; The 'Tables' table in the Standard Schema does not contain a 'TABLE_NAME' column."); List tableNames = new List(); foreach (DataRow row in table.Rows) tableNames.Add((string)row["TABLE_NAME"]); return tableNames.ToArray(); } // // Command // private void SetDataType(DAL.CommandInfo info, DataRow columnRow, int index) { string dataTypeId = (string)columnRow["DataTypeId"]; string nativeType = Spludlow.Data.Schemas.CommonToNative(dataTypeId, "OdbcType"); OdbcType dbType = (OdbcType)Enum.Parse(typeof(OdbcType), nativeType); info.ParameterTypes[index] = (int)dbType; info.ParameterMaxLengths[index] = (int)columnRow["MaxLength"]; } public DAL.CommandInfo MakeCommandInfo(string commandText) { return MakeCommandInfo(commandText, DALCommon.ExtractParameterNames(commandText)); } public DAL.CommandInfo MakeCommandInfo(string commandText, string[] parameterNames) { return Spludlow.Data.DALCommon.MakeCommandInfo(commandText, parameterNames, this.Schema(), this.SetDataType); } private OdbcCommand MakeCommandNative(string commandText) { OdbcCommand command = new OdbcCommand(commandText, this.Connection, this.Transaction); // The time in seconds to wait for the command to execute. The default is 30 seconds. //command.CommandTimeout = 20 * 60; return command; } public DbCommand MakeCommand(string commandText) { return this.MakeCommandNative(commandText); } public DbCommand MakeCommand(string commandText, object[] parameterValues) { DAL.CommandInfo commandInfo = this.MakeCommandInfo(commandText); return this.MakeCommand(commandInfo, parameterValues); } public DbCommand MakeCommand(string commandText, string[] parameterNames, object[] parameterValues) { DAL.CommandInfo commandInfo = this.MakeCommandInfo(commandText, parameterNames); return this.MakeCommand(commandInfo, parameterValues); } public DbCommand MakeCommand(DAL.CommandInfo commandInfo, object[] parameterValues) { if (commandInfo.ParameterNames.Length != parameterValues.Length) throw new ApplicationException("Making Command with different parameterNames and parameterValues counts: " + commandInfo.ParameterNames.Length + ", " + parameterValues.Length); OdbcCommand command = this.MakeCommandNative(commandInfo.CommandText); for (int index = 0; index < commandInfo.ParameterNames.Length; ++index) { string paramterName = commandInfo.ParameterNames[index]; OdbcType dbType = (OdbcType)commandInfo.ParameterTypes[index]; int maxLength = commandInfo.ParameterMaxLengths[index]; OdbcParameter parameter = command.CreateParameter(); parameter.ParameterName = paramterName; if ((int)dbType != -1) parameter.OdbcType = dbType; // seems to make slightly slower ???????????????????????????????? try differnt DALs //if (maxLength > 0) // parameter.Size = maxLength; // using Size seems to slow things down big time on Insert DAL Test using dummpy Logs and bodies. Maybe becasue using max size? Do more tests !!!!!!!!!!!!!! parameter.Value = parameterValues[index]; command.Parameters.Add(parameter); } return command; } // // ExecuteScalar // public object ExecuteScalar(string commandText) { using (OdbcCommand command = (OdbcCommand)this.MakeCommand(commandText)) return this.ExecuteScalar(command); } public object ExecuteScalar(string commandText, object[] parameterValues) { using (OdbcCommand command = (OdbcCommand)this.MakeCommand(commandText, parameterValues)) return this.ExecuteScalar(command); } public object ExecuteScalar(string commandText, string[] parameterNames, object[] parameterValues) { using (OdbcCommand command = (OdbcCommand)this.MakeCommand(commandText, parameterNames, parameterValues)) return this.ExecuteScalar(command); } public object ExecuteScalar(DAL.CommandInfo commandInfo, object[] parameterValues) { using (OdbcCommand command = (OdbcCommand)this.MakeCommand(commandInfo, parameterValues)) return this.ExecuteScalar(command); } public object ExecuteScalar(DbCommand command) { return this.ExecuteScalar((OdbcCommand)command); } private object ExecuteScalar(OdbcCommand command) { if (this.Transaction != null) return command.ExecuteScalar(); this.Open(); try { return command.ExecuteScalar(); } finally { this.Close(); } } // // ExecuteNonQuery // public int ExecuteNonQuery(string commandText) { using (OdbcCommand command = (OdbcCommand)this.MakeCommand(commandText)) return this.ExecuteNonQuery(command); } public int ExecuteNonQuery(string commandText, object[] parameterValues) { using (OdbcCommand command = (OdbcCommand)this.MakeCommand(commandText, parameterValues)) return this.ExecuteNonQuery(command); } public int ExecuteNonQuery(string commandText, string[] parameterNames, object[] parameterValues) { using (OdbcCommand command = (OdbcCommand)this.MakeCommand(commandText, parameterNames, parameterValues)) return this.ExecuteNonQuery(command); } public int ExecuteNonQuery(DAL.CommandInfo commandInfo, object[] parameterValues) { using (OdbcCommand command = (OdbcCommand)this.MakeCommand(commandInfo, parameterValues)) return this.ExecuteNonQuery(command); } public int ExecuteNonQuery(DbCommand command) { return this.ExecuteNonQuery((OdbcCommand)command); } private int ExecuteNonQuery(OdbcCommand command) { if (this.Transaction != null) return command.ExecuteNonQuery(); this.Open(); try { return command.ExecuteNonQuery(); } finally { this.Close(); } } // // Fill // private static void FixCommandText(OdbcCommand command) // Do in other places !!!!!!!!!!!!!!!!!!!! { if (command.CommandText.Contains("[") == false) return; StringBuilder text = new StringBuilder(command.CommandText); text.Replace("[", "'"); text.Replace("]", "'"); command.CommandText = text.ToString(); } private void ExecuteAdapter(OdbcCommand command, DataSet dataSet, DataTable table) { FixCommandText(command); if (this.Transaction == null) this.Open(); try { using (OdbcDataAdapter adapter = new OdbcDataAdapter(command)) { if (dataSet != null) adapter.Fill(dataSet); if (table != null) adapter.Fill(table); } } finally { if (this.Transaction == null) this.Close(); } } public void Fill(DataSet dataSet, string commandText) { using (OdbcCommand command = (OdbcCommand)this.MakeCommand(commandText)) this.Fill(dataSet, command); } public void Fill(DataSet dataSet, string commandText, object[] parameterValues) { using (OdbcCommand command = (OdbcCommand)this.MakeCommand(commandText, parameterValues)) this.Fill(dataSet, command); } public void Fill(DataSet dataSet, string commandText, string[] parameterNames, object[] parameterValues) { using (OdbcCommand command = (OdbcCommand)this.MakeCommand(commandText, parameterNames, parameterValues)) this.Fill(dataSet, command); } public void Fill(DataSet dataSet, DAL.CommandInfo commandInfo, object[] parameterValues) { using (OdbcCommand command = (OdbcCommand)this.MakeCommand(commandInfo, parameterValues)) this.Fill(dataSet, command); } public void Fill(DataSet dataSet, DbCommand command) { this.Fill(dataSet, (OdbcCommand)command); } private void Fill(DataSet dataSet, OdbcCommand command) { this.ExecuteAdapter(command, dataSet, null); } public void Fill(DataTable table, string commandText) { using (OdbcCommand command = (OdbcCommand)this.MakeCommand(commandText)) this.Fill(table, command); } public void Fill(DataTable table, string commandText, object[] parameterValues) { using (OdbcCommand command = (OdbcCommand)this.MakeCommand(commandText, parameterValues)) this.Fill(table, command); } public void Fill(DataTable table, string commandText, string[] parameterNames, object[] parameterValues) { using (OdbcCommand command = (OdbcCommand)this.MakeCommand(commandText, parameterNames, parameterValues)) this.Fill(table, command); } public void Fill(DataTable table, DAL.CommandInfo commandInfo, object[] parameterValues) { using (OdbcCommand command = (OdbcCommand)this.MakeCommand(commandInfo, parameterValues)) this.Fill(table, command); } public void Fill(DataTable table, DbCommand command) { this.Fill(table, (OdbcCommand)command); } private void Fill(DataTable table, OdbcCommand command) { this.ExecuteAdapter(command, null, table); } // // Select // public DataTable Select(string commandText) { using (OdbcCommand command = (OdbcCommand)this.MakeCommand(commandText)) return this.Select(command); //try //{ // using (OdbcCommand command = (OdbcCommand)this.MakeCommand(commandText)) // return this.Select(command); //} //catch (OdbcException ee) //{ // throw new FaultException(ee); //} } public DataTable Select(string commandText, object[] parameterValues) { using (OdbcCommand command = (OdbcCommand)this.MakeCommand(commandText, parameterValues)) return this.Select(command); } public DataTable Select(string commandText, string[] parameterNames, object[] parameterValues) { using (OdbcCommand command = (OdbcCommand)this.MakeCommand(commandText, parameterNames, parameterValues)) return this.Select(command); } public DataTable Select(DAL.CommandInfo commandInfo, object[] parameterValues) { using (OdbcCommand command = (OdbcCommand)this.MakeCommand(commandInfo, parameterValues)) return this.Select(command); } public DataTable Select(DbCommand command) { return this.Select((OdbcCommand)command); } private DataTable Select(OdbcCommand command) { DataSet dataSet = this.SelectDS(command); DataTable table = dataSet.Tables[0]; dataSet.Tables.Remove(table); return table; } public DataSet SelectDS(string commandText) { lock (this.Lock) { using (OdbcCommand command = (OdbcCommand)this.MakeCommand(commandText)) return this.SelectDS(command); } } public DataSet SelectDS(string commandText, object[] parameterValues) { using (OdbcCommand command = (OdbcCommand)this.MakeCommand(commandText, parameterValues)) return this.SelectDS(command); } public DataSet SelectDS(string commandText, string[] parameterNames, object[] parameterValues) { using (OdbcCommand command = (OdbcCommand)this.MakeCommand(commandText, parameterNames, parameterValues)) return this.SelectDS(command); } public DataSet SelectDS(DAL.CommandInfo commandInfo, object[] parameterValues) { using (OdbcCommand command = (OdbcCommand)this.MakeCommand(commandInfo, parameterValues)) return this.SelectDS(command); } public DataSet SelectDS(DbCommand command) { return this.SelectDS((OdbcCommand)command); } private DataSet SelectDS(OdbcCommand command) { DataSet dataSet = new DataSet(); this.ExecuteAdapter(command, dataSet, null); return dataSet; } public void Backup(string serverFilename) { throw new NotImplementedException(); } public void ChangeDatabase(string databaseName) { throw new NotImplementedException(); } public string CurrentDatabase() { throw new NotImplementedException(); } public void DatabaseCreate(string databaseName) { throw new NotImplementedException(); } public void DatabaseDelete(string databaseName) { throw new NotImplementedException(); } public bool DatabaseExists(string databaseName) { throw new NotImplementedException(); } public string[] DatabaseList() { throw new NotImplementedException(); } public long Insert(DataRow row) { throw new NotImplementedException(); } public long Insert(string tableName, object[] row) { throw new NotImplementedException(); } public long Insert(DAL.CommandInfo insertInfo, object[] row) { throw new NotImplementedException(); } public long Insert(DAL.CommandInfo insertInfo, DataRow row) { throw new NotImplementedException(); } public long Insert(string tableName, DataRow row) { throw new NotImplementedException(); } public long InsertOrUpdate(DataRow row) { throw new NotImplementedException(); } public long InsertOrUpdate(string tableName, DataRow row) { throw new NotImplementedException(); } public void LoginCreate(string loginName, bool administrator) { throw new NotImplementedException(); } public void LoginCreate(string loginName, string password, bool administrator) { throw new NotImplementedException(); } public void LoginDelete(string loginName) { throw new NotImplementedException(); } public bool LoginExists(string loginName) { throw new NotImplementedException(); } public string[] LoginList() { throw new NotImplementedException(); } public DAL.CommandInfo MakeInserter(string tableName) { throw new NotImplementedException(); } public DAL.CommandInfo MakeUpdater(string tableName) { throw new NotImplementedException(); } public DAL.CommandInfo MakeUpdater(string tableName, string[] updateColumns) { throw new NotImplementedException(); } public void Operation(string details) { throw new NotImplementedException(); } public string PageCommandText(string commandText, int pageIndex, int pageSize) { throw new NotImplementedException(); } public string Quotes(string commandText) { return this.Quotes(commandText, "[", "]"); } public string Quotes(string commandText, string replaceOpen, string replaceClose) { StringBuilder result = new StringBuilder(commandText); result.Replace(replaceOpen, this.QuoteOpen); result.Replace(replaceClose, this.QuoteClose); return result.ToString(); } public void Restore(string serverFilename) { throw new NotImplementedException(); } public string RunScript(string localFilename, string server, string database) { throw new NotImplementedException(); } public string RunScript(string localFilename, string server, string database, string userName, string password) { throw new NotImplementedException(); } public void TableAddKeys(DataSet schema, string keyTypesPUIF) { throw new NotImplementedException(); } public void TableAddKeys(string[] tableNames, DataSet schema, string keyTypesPUIF) { throw new NotImplementedException(); } public void TableAddKeys(string tableName, DataSet schema, string keyTypesPUIF) { throw new NotImplementedException(); } public void TableClear(string tableName) { throw new NotImplementedException(); } public void TableCreate(string tableName, DataSet schema) { throw new NotImplementedException(); } public void TableCreate(string tableName, DataTable schemaColumns, DataTable schemaKeys) { throw new NotImplementedException(); } public void TableDelete(string tableName) { throw new NotImplementedException(); } public bool TableExists(string tableName) { throw new NotImplementedException(); } public int Update(DataRow row) { throw new NotImplementedException(); } public int Update(DAL.CommandInfo info, object[] row) { throw new NotImplementedException(); } public int Update(DAL.CommandInfo info, DataRow row) { throw new NotImplementedException(); } public int Update(string tableName, object[] row) { throw new NotImplementedException(); } public int Update(string tableName, DataRow row) { throw new NotImplementedException(); } public int Update(string tableName, string[] columns, object[] row) { throw new NotImplementedException(); } public int Update(string tableName, string[] columns, DataRow row) { throw new NotImplementedException(); } public void UserCreate(string userName, string loginName, bool administrator) { throw new NotImplementedException(); } public void UserDelete(string userName) { throw new NotImplementedException(); } public bool UserExists(string userName) { throw new NotImplementedException(); } public string[] UserList() { throw new NotImplementedException(); } public void BulkInsert(DataTable table, string tableName, string tempDirectory) { throw new NotImplementedException(); } public void DatabaseCreate(string databaseName, DataSet schema) { throw new NotImplementedException(); } } }