// 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.Data; using System.Data.Common; using System.Linq; using System.Text; using System.Threading.Tasks; using System.IO; using MySql.Data.MySqlClient; namespace Spludlow.Data { /// /// MySQL DAL Implimentaion - fairly complete, logins and users not done yet /// /// Only require referencing a single pure .net DLL /// https://dev.mysql.com/downloads/connector/net/ /// Use the .Net & Mono (Architecture Independent), ZIP Archive /// /// Backup requires "C:\Users\SpludlowUser\my.ini" is configured /// public class DALMySQL : Spludlow.Data.IDAL { private MySqlConnection Connection; private MySqlTransaction Transaction = null; private string ChangedDatabase = null; private string CurrentDatabaseCache = null; private string CacheKeyPrefix; private Dictionary ConvertDataTypes; private string QuoteOpen = "`"; private string QuoteClose = "`"; private int CommandTimeoutSeconds = -1; private List SystemDatabaseNames = new List(new string[] { "information_schema", "mysql", "performance_schema", "sys", }); public DALMySQL() { } public DALMySQL(string connectionString) { this.Initialize(connectionString); } public void Initialize(string connectionString) { if (connectionString == null || connectionString.Length == 0) // For use with utility methods return; if (connectionString.StartsWith("@") == true) connectionString = Spludlow.Config.ConnectionString(connectionString.Substring(1)); this.Connection = new MySqlConnection(connectionString); this.CacheKeyPrefix = "DALMySQL-Schemas-" + this.DataSource() + "-"; this.ConvertDataTypes = new Dictionary(); string[] convertLookups = new string[] { "blob longblob", "tinyblob longblob", "mediumblob longblob", "longblob longblob", "text longtext", "tinytext longtext", "mediumtext longtext", "longtext longtext", "timestamp datetime", // matter ??? "set varchar", "enum varchar", "year int", }; foreach (string convertLookup in convertLookups) { string[] words = Spludlow.Text.Split(convertLookup, '\t', true, false); this.ConvertDataTypes.Add(words[0], words[1]); } this.ChangeDatabase(null); } //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 static string MakeConnectionString(string host, string database, string userName, string password) { string[] names = new string[] { "Server", "Database", "Uid", "Pwd" }; string[] values = new string[] { host, database, userName, password }; StringBuilder connectionString = new StringBuilder(); for (int index = 0; index < values.Length; ++index) { string value = values[index]; if (value == null) continue; connectionString.Append(names[index]); connectionString.Append("='"); connectionString.Append(value); connectionString.Append("';"); } return connectionString.ToString(); } private void Open() { this.Connection.Open(); if (this.ChangedDatabase != null) { try { string database = this.ChangedDatabase; if (database == "") database = "mysql"; this.Connection.ChangeDatabase(database); } catch { this.Connection.Close(); throw; } } } private void Close() { this.Connection.Close(); } public string DataSource() { return this.Connection.DataSource; } public void CommandTimeout(int minutes) { if (minutes < 0) this.CommandTimeoutSeconds = -1; else this.CommandTimeoutSeconds = minutes * 60; } // // Current // public string CurrentDatabase() { return this.CurrentDatabaseCache; } public void ChangeDatabase(string databaseName) { this.ChangedDatabase = databaseName; object result = this.ExecuteScalar("SELECT DATABASE();"); if (result == null || result is DBNull) databaseName = null; else databaseName = (string)result; if (databaseName == "mysql") databaseName = null; // Zero length ? test this.CurrentDatabaseCache = databaseName; } // // Transactions // public void Begin() { if (this.Transaction != null) throw new ApplicationException("DALMySql: 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("DALMySql: Commit Transaction not present."); try { this.Transaction.Commit(); this.Transaction = null; } finally { this.Close(); } } public void Rollback() { if (this.Transaction == null) throw new ApplicationException("DALMySql: Rollback Transaction not present."); try { this.Transaction.Rollback(); this.Transaction = null; } finally { this.Close(); } } // // Schema // public DataSet Schema() { string databaseName = this.CurrentDatabase(); if (databaseName == null) throw new ApplicationException("DAL MySql; To get the schema you must have a current database"); string key = this.CacheKeyPrefix + databaseName; DataSet schema = (DataSet)Spludlow.Caching.Get(key); if (schema != null) return schema; schema = new DataSet(); DataSet schemaReader = this.SchemaReader(); DataSet schemaNative = this.SchemaNative(); DataSet schemaStandard = this.SchemaConnection(); foreach (string tableName in this.TableList()) { this.SchemaColumns(schema, tableName, schemaReader, schemaNative); this.SchemaKeys(schema, tableName, schemaStandard); } Spludlow.Caching.Set(key, schema); return 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; } private void SchemaColumns(DataSet schema, string tableName, DataSet schemaReader, DataSet schemaNative) { DataTable schemaTable = Spludlow.Data.Schemas.NewSchemaColumnsTable(tableName); foreach (DataRow readerRow in schemaReader.Tables[tableName].Rows) { string columnName = (string)readerRow["ColumnName"]; int columnSize = (int)readerRow["ColumnSize"]; DataRow nativeRow = schemaNative.Tables["COLUMNS"].Rows.Find(new object[] { tableName, columnName }); if (nativeRow == null) throw new ApplicationException("DAL MySql Schema; Can not find native Schema row for column: " + tableName + "." + columnName); string typeName = (string)nativeRow["DATA_TYPE"]; if (this.ConvertDataTypes.ContainsKey(typeName) == true) typeName = this.ConvertDataTypes[typeName]; if (typeName == "tinyint" && columnSize == 1) typeName += "(1)"; string columnType = (string)nativeRow["COLUMN_TYPE"]; if (columnType.Contains("unsigned") == true) typeName += " unsigned"; DataRow schemaRow = schemaTable.NewRow(); schemaRow["ColumnName"] = columnName; schemaRow["Ordinal"] = (int)readerRow["ColumnOrdinal"]; schemaRow["DataTypeId"] = Spludlow.Data.Schemas.NativeToCommon(typeName, "MySql"); schemaRow["MaxLength"] = columnSize; schemaRow["Precision"] = (int)readerRow["NumericPrecision"]; schemaRow["Scale"] = (int)readerRow["NumericScale"]; schemaRow["PrimaryKey"] = (bool)readerRow["IsKey"]; schemaRow["AllowDBNull"] = (bool)readerRow["AllowDBNull"]; schemaRow["AutoIncrement"] = (bool)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 schemaStandard) { DataTable keysTable = Spludlow.Data.Schemas.NewSchemaKeysTable(tableName); foreach (DataRow indexRow in schemaStandard.Tables["Indexes"].Select("TABLE_NAME = '" + tableName + "'")) { string indexName = (string)indexRow["INDEX_NAME"]; string keyType = "I"; if ((bool)indexRow["PRIMARY"] == true) { keyType = "P"; } else { if ((bool)indexRow["UNIQUE"] == true) keyType = "U"; } foreach (DataRow indexColumnRow in schemaStandard.Tables["IndexColumns"].Select("TABLE_NAME = '" + tableName + "' AND INDEX_NAME = '" + indexName + "'", "ORDINAL_POSITION")) { string columnName = (string)indexColumnRow["COLUMN_NAME"]; int ordinal = (int)indexColumnRow["ORDINAL_POSITION"]; bool descending = false; if ((string)indexColumnRow["SORT_ORDER"] != "A") descending = true; // MySQL allows same index across tables, other DB types no not, so prefix the table name keysTable.Rows.Add(new object[] { tableName + "_" + indexName, keyType, ordinal, descending, columnName, null, null }); } } foreach (DataRow foreignKeyRow in schemaStandard.Tables["Foreign Keys"].Select("table_name = '" + tableName + "'")) { string constraintName = (string)foreignKeyRow["constraint_name"]; foreach (DataRow keyColumnRow in schemaStandard.Tables["Foreign Key Columns"].Select("CONSTRAINT_NAME = '" + constraintName + "' AND TABLE_NAME = '" + tableName + "'", "ORDINAL_POSITION")) { string columnName = (string)keyColumnRow["COLUMN_NAME"]; string parentTable = (string)keyColumnRow["REFERENCED_TABLE_NAME"]; string parentColumn = (string)keyColumnRow["REFERENCED_COLUMN_NAME"]; int ordinal = (int)(Int64)keyColumnRow["ORDINAL_POSITION"]; // MySQL dopes !!!! keysTable.Rows.Add(new object[] { constraintName, "F", ordinal, false, columnName, parentTable, parentColumn }); } } schema.Tables.Add(keysTable); } public DataSet SchemaNative() { string databaseName = this.CurrentDatabase(); if (databaseName == null) throw new ApplicationException("DAL MySql; To get the Native schema you must have a current database"); //"CHARACTER_SETS", //"COLLATIONS", //"COLLATION_CHARACTER_SET_APPLICABILITY", //"COLUMNS", //"COLUMN_PRIVILEGES", //"ENGINES", //"EVENTS", //"FILES", //"GLOBAL_STATUS", //"GLOBAL_VARIABLES", //"KEY_COLUMN_USAGE", //"ndb_transid_mysql_connection_map", //"OPTIMIZER_TRACE", //"PARAMETERS", //"PARTITIONS", //"PLUGINS", //"PROCESSLIST", //"PROFILING", //"REFERENTIAL_CONSTRAINTS", //"ROUTINES", //"SCHEMATA", //"SCHEMA_PRIVILEGES", //"STATISTICS", //"TABLES", //"TABLESPACES", //"TABLE_CONSTRAINTS", //"TABLE_PRIVILEGES", //"TRIGGERS", //"USER_PRIVILEGES", //"VIEWS", string[] systemTables = new string[] { "COLUMNS TABLE_SCHEMA", "KEY_COLUMN_USAGE CONSTRAINT_SCHEMA", "REFERENTIAL_CONSTRAINTS CONSTRAINT_SCHEMA", "SCHEMATA SCHEMA_NAME", "TABLES TABLE_SCHEMA", "TABLE_CONSTRAINTS CONSTRAINT_SCHEMA", }; DataSet dataSet = new DataSet(); string commandTemplate = "SELECT * FROM information_schema.@TableName WHERE (@FilterColumn = '@DatabaseName')"; foreach (string systemTableInfo in systemTables) { string[] words = Spludlow.Text.Split(systemTableInfo, '\t', true); string systemTableName = words[0]; string filterColumn = words[1]; string commandText = commandTemplate; commandText = commandText.Replace("@TableName", systemTableName); commandText = commandText.Replace("@FilterColumn", filterColumn); commandText = commandText.Replace("@DatabaseName", databaseName); this.Fill(dataSet, commandText); Spludlow.Data.ADO.NameLastTable(dataSet, systemTableName); } DataTable table; table = dataSet.Tables["TABLES"]; table.PrimaryKey = new DataColumn[] { table.Columns["TABLE_NAME"] }; table = dataSet.Tables["COLUMNS"]; table.PrimaryKey = new DataColumn[] { table.Columns["TABLE_NAME"], table.Columns["COLUMN_NAME"] }; return dataSet; } public DataSet SchemaConnection() { string databaseName = this.CurrentDatabase(); if (databaseName == null) throw new ApplicationException("DAL MySql; 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); } finally { if (this.Transaction == null) this.Close(); } DataTable table; string[] fixTables = new string[] { "Views TABLE_SCHEMA", "ViewColumns VIEW_SCHEMA", "Procedure Parameters SPECIFIC_SCHEMA", "Procedures ROUTINE_SCHEMA", "Triggers TRIGGER_SCHEMA", }; foreach (string fixTableInfo in fixTables) { string[] words = Spludlow.Text.Split(fixTableInfo, '\t', true); string tableName = words[0]; string filterColumn = words[1]; table = schema.Tables[tableName]; DataTable copyTable = table.Copy(); table.Clear(); foreach (DataRow row in copyTable.Rows) { if (row.IsNull(filterColumn) == false && (string)row[filterColumn] == databaseName) table.ImportRow(row); } } table = schema.Tables["Columns"]; table.PrimaryKey = new DataColumn[] { table.Columns["TABLE_NAME"], table.Columns["COLUMN_NAME"] }; // Same in all DALS ?? !!! return schema; } public DataSet SchemaReader() { string[] tableNames = this.TableList(); if (this.Transaction == null) this.Open(); try { return Spludlow.Data.DALCommon.SchemaReader(this.Connection, this.Transaction, tableNames, "`", "`"); } finally { if (this.Transaction == null) this.Close(); } } public void SchemaRefresh() { string databaseName = this.CurrentDatabase(); if (databaseName == null) throw new ApplicationException("DAL MySql; To Refresh the schema you must have a current database"); string key = this.CacheKeyPrefix + databaseName; Spludlow.Caching.Remove(key); } // // Databases // public string[] DatabaseList() { DataTable table = this.Select("select SCHEMA_NAME from information_schema.SCHEMATA"); List databases = new List(); foreach (DataRow row in table.Rows) { string name = (string)row[0]; if (SystemDatabaseNames.Contains(name) == false) databases.Add(name); } return databases.ToArray(); } public void DatabaseCreate(string databaseName) { string commandText = "CREATE SCHEMA " + databaseName; this.ExecuteNonQuery(commandText); } public void DatabaseCreate(string databaseName, DataSet schema) { Spludlow.Data.DALCommon.DatabaseCreate(this, databaseName, schema); } public void DatabaseDelete(string databaseName) { string commandText = "DROP DATABASE " + databaseName; this.ExecuteNonQuery(commandText); } public bool DatabaseExists(string databaseName) { string commandText = "SELECT SCHEMA_NAME FROM information_schema.SCHEMATA WHERE SCHEMA_NAME = '" + databaseName + "'"; object result = this.ExecuteScalar(commandText); if (result == null || result is DBNull) return false; return true; } // // Tables // public void TableClear(string tableName) { this.ExecuteNonQuery("TRUNCATE TABLE `" + tableName + "`"); // MySQL handles foriegn keys for TRUNCATE itself ? } public void TableCreate(string tableName, DataSet schema) { this.TableCreate(tableName, schema.Tables[tableName + "_Columns"], schema.Tables[tableName + "_Keys"]); } public void TableCreate(string tableName, DataTable schemaColumns, DataTable schemaKeys) { string databaseName = this.CurrentDatabase(); if (databaseName == null) throw new ApplicationException("DAL MySQL; To create a table you must have a current database"); Spludlow.Data.DALCommon.CreateTableInfo info = new DALCommon.CreateTableInfo(); info.DatabaseType = "MySql"; info.QuoteOpen = "`"; info.QuoteClose = "`"; info.AllowNull = "DEFAULT NULL"; info.DontAllowNull = "NOT NULL"; info.Identity = "AUTO_INCREMENT"; info.ColumnLine = "@ColumnName @DataType @Null @Identity"; info.PrimaryLine = "CONSTRAINT @KeyName PRIMARY KEY (@Columns)"; // "PRIMARY KEY (@Columns)"; info.UniqueLine = null; // "UNIQUE KEY @KeyName (@Columns)"; info.ForeignLine = null; // "FOREIGN KEY @KeyName (@Columns) REFERENCES @ParentTable(@ParentColumns)"; info.IndexLine = null; // "INDEX @KeyName (@Columns)"; string commandText = Spludlow.Data.DALCommon.MakeCreateTableCommandText(tableName, schemaColumns, schemaKeys, info); Spludlow.Log.Report("DAL; Creating Table: " + tableName, new object[] { schemaColumns, schemaKeys, commandText }); this.ExecuteNonQuery(commandText); } public void TableDelete(string tableName) { string databaseName = this.CurrentDatabase(); if (databaseName == null) throw new ApplicationException("DAL MySQL; To delete a table you must have a current database"); string commandText = "DROP TABLE `" + tableName + "`"; this.ExecuteNonQuery(commandText); } public bool TableExists(string tableName) { string databaseName = this.CurrentDatabase(); if (databaseName == null) throw new ApplicationException("DAL MySQL; To perform a TableExists you must have a current database"); string commandText = "SELECT TABLE_NAME FROM information_schema.TABLES WHERE (TABLE_SCHEMA = @DatabaseName AND TABLE_NAME = @TableName)"; object result = this.ExecuteScalar(commandText, new object[] { databaseName, tableName }); if (result == null || result is DBNull) return false; return true; } public string[] TableList() { string database = this.CurrentDatabase(); if (database == null) throw new ApplicationException("DAL MySQL; To list tables you must have a current database"); DataTable table = this.Select("select TABLE_NAME from information_schema.TABLES WHERE TABLE_SCHEMA = '" + database + "'"); List tableNames = new List(); foreach (DataRow row in table.Rows) tableNames.Add((string)row[0]); return tableNames.ToArray(); } public void TableAddKeys(DataSet schema, string keyTypesPUIF) { string[] tableNames = Spludlow.Data.Schemas.OrderTablesOnAncestors(this.TableList(), schema); // Need ? this.TableAddKeys(tableNames, schema, keyTypesPUIF); } public void TableAddKeys(string tableName, DataSet schema, string keyTypesPUIF) { this.TableAddKeys(new string[] { tableName }, schema, keyTypesPUIF); } public void TableAddKeys(string[] tableNames, DataSet schema, string keyTypesPUIF) { string commandTextPrimary = "ALTER TABLE `@TableName` ADD CONSTRAINT `@KeyName` PRIMARY KEY (@Columns)"; string commandTextUnique = "CREATE UNIQUE INDEX `@KeyName` ON `@TableName` (@Columns)"; string commandTextIndex = "CREATE INDEX `@KeyName` ON `@TableName` (@Columns)"; string commandTextForeign = "ALTER TABLE `@TableName` ADD CONSTRAINT `@KeyName` FOREIGN KEY (@Columns) REFERENCES `@ParentTable` (@ParentColumns)"; foreach (string commandText in DALCommon.TableAddKeysCommandText(tableNames, schema, keyTypesPUIF, commandTextPrimary, commandTextUnique, commandTextIndex, commandTextForeign, this.QuoteOpen, this.QuoteClose)) this.ExecuteNonQuery(commandText); } // // Login // 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(); } // // User // 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(); } // // Util // public void Backup(string serverFilename) { string databaseName = this.CurrentDatabase(); if (databaseName == null) throw new ApplicationException("DAL MySQL; To perform a Backup you must have a current database"); string mysqldumpPath = this.BinDirectory() + @"\mysqldump.exe"; if (File.Exists(mysqldumpPath) == false) throw new ApplicationException("DAL MySQL, Backup; Can't find mysqldump: " + mysqldumpPath); string defaultsFilename = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile) + @"\" + "my.ini"; if (File.Exists(defaultsFilename) == false) throw new ApplicationException("DAL MySQL, Backup; Can't find defaults File, it is required for backup and should contain credentials: " + defaultsFilename); StringBuilder arguments = new StringBuilder("--defaults-file=\"@DefaultsFile\" --host=\"@Server\" --result-file=\"@Filename\" \"@Database\""); arguments.Replace("@DefaultsFile", defaultsFilename); arguments.Replace("@Server", this.DataSource()); arguments.Replace("@Filename", serverFilename); arguments.Replace("@Database", databaseName); Spludlow.SpawnProcess.ProcessExitInfo info = Spludlow.SpawnProcess.Run(mysqldumpPath, arguments.ToString(), 0); // Can hang if something has DB locked if (info.StandardError.Length == 0 && info.ExitCode == 0) return; throw new ApplicationException("Archive, Create 7Zip, ExitCode:" + info.ExitCode + ", " + Environment.NewLine + "StandardError:" + info.StandardError); // + Environment.NewLine + "StandardOutput:" + info.StandardOutput); } public void Restore(string serverFilename) { string databaseName = this.CurrentDatabase(); if (databaseName == null) throw new ApplicationException("DAL MySQL; To perform a Restore you must have a current database"); string server = this.DataSource(); this.RunScript(serverFilename, server, databaseName); } public string RunScript(string localFilename, string server, string database) // Utility method not using any object varibles, can use with null constructor { return this.RunScript(localFilename, server, database, null, null); } public string RunScript(string localFilename, string server, string database, string userName, string password) { string mysqldumpPath = this.BinDirectory() + @"\mysql.exe"; string defaultsFilename = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile) + @"\" + "my.ini"; string execute = "source " + localFilename; string[] argumentNames; string[] argumentValues; if (userName == null) { argumentNames = new string[] { "--defaults-file", "--host", "--database", "--execute" }; argumentValues = new string[] { defaultsFilename, server, database, execute }; } else { argumentNames = new string[] { "--user", "--password", "--host", "--database", "--execute" }; argumentValues = new string[] { userName, password, server, database, execute }; } StringBuilder arguments = new StringBuilder(); for (int index = 0; index < argumentNames.Length; ++index) { if (arguments.Length > 0) arguments.Append(" "); arguments.Append(argumentNames[index]); arguments.Append("=\""); arguments.Append(argumentValues[index]); arguments.Append("\""); } string argumentsText = arguments.ToString(); Spludlow.Log.Info("DAL MySQL; Running Script: " + mysqldumpPath + " " + argumentsText); Spludlow.SpawnProcess.ProcessExitInfo exitInfo = Spludlow.SpawnProcess.Run(mysqldumpPath, argumentsText, 0); // No timeout !!!! if (exitInfo.StandardError.Length > 0) Spludlow.Log.Error("DAL MySQL; RunScript, Error in output", exitInfo.StandardError); if (exitInfo.ExitCode != 0) throw new ApplicationException("DAL SQL; RunScript, Bad Exit Code: " + exitInfo.ExitCode); return exitInfo.StandardOutput; } private string BinDirectory() { string binDirectory = Spludlow.Config.Get("MySQLBinDirectory", true); if (binDirectory != null) return binDirectory; foreach (string productDirectory in Directory.GetDirectories(@"C:\Program Files\MySQL", "MySQL Server *")) { string posibleDirectory = productDirectory + @"\bin"; if (Directory.Exists(posibleDirectory) == true) { if (binDirectory != null) Spludlow.Log.Warning("DAL MySQL, Finding BIN Directory; Multiple matches found"); binDirectory = posibleDirectory; } } if (binDirectory == null) throw new ApplicationException("DAL MySQL, Finding BIN Directory; Did not found, you can use the config key 'MySQLBinDirectory' to specify it"); return binDirectory; } public void Operation(string details) { if (details.StartsWith("identity insert") == true) // Don't need to do anything for MySQL return; if (details.StartsWith("shrink") == true) return; throw new ApplicationException("DAL SQL Operation; Unknown: " + details); } public string PageCommandText(string commandText, int pageIndex, int pageSize) { int offset = pageIndex * pageSize; StringBuilder text = new StringBuilder(commandText); text.Append(" LIMIT @Offset,@PageSize"); text.Replace("@Offset", offset.ToString()); text.Replace("@PageSize", pageSize.ToString()); return text.ToString(); } // // Command // private static void FixCommandText(MySqlCommand command) { if (command.CommandText.Contains("[") == false) return; StringBuilder text = new StringBuilder(command.CommandText); text.Replace("[", "`"); text.Replace("]", "`"); command.CommandText = text.ToString(); } private void SetDataType(DAL.CommandInfo info, DataRow columnRow, int index) { string dataTypeId = (string)columnRow["DataTypeId"]; string nativeType = Spludlow.Data.Schemas.CommonToNative(dataTypeId, "MySqlDbType"); MySqlDbType sqlDbType = (MySqlDbType)Enum.Parse(typeof(MySqlDbType), nativeType); info.ParameterTypes[index] = (int)sqlDbType; 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 MySqlCommand MakeCommandNative(string commandText) { MySqlCommand command = new MySqlCommand(commandText, this.Connection, this.Transaction); if (this.CommandTimeoutSeconds >= 0) command.CommandTimeout = this.CommandTimeoutSeconds; 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); MySqlCommand command = this.MakeCommandNative(commandInfo.CommandText); for (int index = 0; index < commandInfo.ParameterNames.Length; ++index) { string paramterName = commandInfo.ParameterNames[index]; MySqlDbType dbType = (MySqlDbType)commandInfo.ParameterTypes[index]; int maxLength = commandInfo.ParameterMaxLengths[index]; MySqlParameter parameter = command.CreateParameter(); parameter.ParameterName = paramterName; if ((int)dbType != -1) parameter.MySqlDbType = 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 (MySqlCommand command = (MySqlCommand)this.MakeCommand(commandText)) return this.ExecuteScalar(command); } public object ExecuteScalar(string commandText, object[] parameterValues) { using (MySqlCommand command = (MySqlCommand)this.MakeCommand(commandText, parameterValues)) return this.ExecuteScalar(command); } public object ExecuteScalar(string commandText, string[] parameterNames, object[] parameterValues) { using (MySqlCommand command = (MySqlCommand)this.MakeCommand(commandText, parameterNames, parameterValues)) return this.ExecuteScalar(command); } public object ExecuteScalar(DAL.CommandInfo commandInfo, object[] parameterValues) { using (MySqlCommand command = (MySqlCommand)this.MakeCommand(commandInfo, parameterValues)) return this.ExecuteScalar(command); } public object ExecuteScalar(DbCommand command) { return this.ExecuteScalar((MySqlCommand)command); } private object ExecuteScalar(MySqlCommand command) { FixCommandText(command); if (this.Transaction != null) return command.ExecuteScalar(); this.Open(); try { return command.ExecuteScalar(); } finally { this.Close(); } } // // ExecuteNonQuery // public int ExecuteNonQuery(string commandText) { using (MySqlCommand command = (MySqlCommand)this.MakeCommand(commandText)) return this.ExecuteNonQuery(command); } public int ExecuteNonQuery(string commandText, object[] parameterValues) { using (MySqlCommand command = (MySqlCommand)this.MakeCommand(commandText, parameterValues)) return this.ExecuteNonQuery(command); } public int ExecuteNonQuery(string commandText, string[] parameterNames, object[] parameterValues) { using (MySqlCommand command = (MySqlCommand)this.MakeCommand(commandText, parameterNames, parameterValues)) return this.ExecuteNonQuery(command); } public int ExecuteNonQuery(DAL.CommandInfo commandInfo, object[] parameterValues) { using (MySqlCommand command = (MySqlCommand)this.MakeCommand(commandInfo, parameterValues)) return this.ExecuteNonQuery(command); } public int ExecuteNonQuery(DbCommand command) { return this.ExecuteNonQuery((MySqlCommand)command); } private int ExecuteNonQuery(MySqlCommand command) { FixCommandText(command); if (this.Transaction != null) return command.ExecuteNonQuery(); this.Open(); try { return command.ExecuteNonQuery(); } finally { this.Close(); } } // // Fill // private void ExecuteAdapter(MySqlCommand command, DataSet dataSet, DataTable table) { FixCommandText(command); if (this.Transaction == null) this.Open(); try { using (MySqlDataAdapter adapter = new MySqlDataAdapter(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 (MySqlCommand command = (MySqlCommand)this.MakeCommand(commandText)) this.Fill(dataSet, command); } public void Fill(DataSet dataSet, string commandText, object[] parameterValues) { using (MySqlCommand command = (MySqlCommand)this.MakeCommand(commandText, parameterValues)) this.Fill(dataSet, command); } public void Fill(DataSet dataSet, string commandText, string[] parameterNames, object[] parameterValues) { using (MySqlCommand command = (MySqlCommand)this.MakeCommand(commandText, parameterNames, parameterValues)) this.Fill(dataSet, command); } public void Fill(DataSet dataSet, DAL.CommandInfo commandInfo, object[] parameterValues) { using (MySqlCommand command = (MySqlCommand)this.MakeCommand(commandInfo, parameterValues)) this.Fill(dataSet, command); } public void Fill(DataSet dataSet, DbCommand command) { this.Fill(dataSet, (MySqlCommand)command); } private void Fill(DataSet dataSet, MySqlCommand command) { this.ExecuteAdapter(command, dataSet, null); } public void Fill(DataTable table, string commandText) { using (MySqlCommand command = (MySqlCommand)this.MakeCommand(commandText)) this.Fill(table, command); } public void Fill(DataTable table, string commandText, object[] parameterValues) { using (MySqlCommand command = (MySqlCommand)this.MakeCommand(commandText, parameterValues)) this.Fill(table, command); } public void Fill(DataTable table, string commandText, string[] parameterNames, object[] parameterValues) { using (MySqlCommand command = (MySqlCommand)this.MakeCommand(commandText, parameterNames, parameterValues)) this.Fill(table, command); } public void Fill(DataTable table, DAL.CommandInfo commandInfo, object[] parameterValues) { using (MySqlCommand command = (MySqlCommand)this.MakeCommand(commandInfo, parameterValues)) this.Fill(table, command); } public void Fill(DataTable table, DbCommand command) { this.Fill(table, (MySqlCommand)command); } private void Fill(DataTable table, MySqlCommand command) { this.ExecuteAdapter(command, null, table); } // // Select // public DataTable Select(string commandText) { using (MySqlCommand command = (MySqlCommand)this.MakeCommand(commandText)) return this.Select(command); } public DataTable Select(string commandText, object[] parameterValues) { using (MySqlCommand command = (MySqlCommand)this.MakeCommand(commandText, parameterValues)) return this.Select(command); } public DataTable Select(string commandText, string[] parameterNames, object[] parameterValues) { using (MySqlCommand command = (MySqlCommand)this.MakeCommand(commandText, parameterNames, parameterValues)) return this.Select(command); } public DataTable Select(DAL.CommandInfo commandInfo, object[] parameterValues) { using (MySqlCommand command = (MySqlCommand)this.MakeCommand(commandInfo, parameterValues)) return this.Select(command); } public DataTable Select(DbCommand command) { return this.Select((MySqlCommand)command); } private DataTable Select(MySqlCommand command) { DataSet dataSet = this.SelectDS(command); DataTable table = dataSet.Tables[0]; dataSet.Tables.Remove(table); return table; } public DataSet SelectDS(string commandText) { using (MySqlCommand command = (MySqlCommand)this.MakeCommand(commandText)) return this.SelectDS(command); } public DataSet SelectDS(string commandText, object[] parameterValues) { using (MySqlCommand command = (MySqlCommand)this.MakeCommand(commandText, parameterValues)) return this.SelectDS(command); } public DataSet SelectDS(string commandText, string[] parameterNames, object[] parameterValues) { using (MySqlCommand command = (MySqlCommand)this.MakeCommand(commandText, parameterNames, parameterValues)) return this.SelectDS(command); } public DataSet SelectDS(DAL.CommandInfo commandInfo, object[] parameterValues) { using (MySqlCommand command = (MySqlCommand)this.MakeCommand(commandInfo, parameterValues)) return this.SelectDS(command); } public DataSet SelectDS(DbCommand command) { return this.SelectDS((MySqlCommand)command); } private DataSet SelectDS(MySqlCommand command) { DataSet dataSet = new DataSet(); this.ExecuteAdapter(command, dataSet, null); return dataSet; } // // Inserter // public DAL.CommandInfo MakeInserter(string tableName) { return DALCommon.MakeInserter(this, tableName, "; SELECT LAST_INSERT_ID()"); } public long Insert(DataRow row) { return Insert(row.Table.TableName, row); } public long Insert(string tableName, DataRow row) { DAL.CommandInfo insertInfo = this.MakeInserter(tableName); return Insert(insertInfo, row); } public long Insert(DAL.CommandInfo insertInfo, DataRow row) { object[] objectRow = DALCommon.ObjectsFromRow(row, insertInfo); return Insert(insertInfo, objectRow); } public long Insert(string tableName, object[] row) { DAL.CommandInfo insertInfo = this.MakeInserter(tableName); return Insert(insertInfo, row); } public long Insert(DAL.CommandInfo insertInfo, object[] row) { using (MySqlCommand command = (MySqlCommand)this.MakeCommand(insertInfo, row)) { if (insertInfo.Identity == false) { this.ExecuteNonQuery(command); return 0; } else { object obj = this.ExecuteScalar(command); if (obj == null) throw new ApplicationException("DAL Insert; Identity object is null."); if (obj is UInt64) return (long)((UInt64)obj); throw new ApplicationException("DAL Insert; Identity object bad type:\t" + obj.GetType().Name); } } } // // Update // public DAL.CommandInfo MakeUpdater(string tableName) { return MakeUpdater(tableName, new string[0]); } public DAL.CommandInfo MakeUpdater(string tableName, string[] updateColumns) { return DALCommon.MakeUpdater(this, this.Schema(), tableName, updateColumns); } public int Update(DataRow row) { return this.Update(row.Table.TableName, row); } public int Update(string tableName, DataRow row) { DAL.CommandInfo updateInfo = this.MakeUpdater(tableName); return this.Update(updateInfo, row); } public int Update(string tableName, string[] columns, DataRow row) { DAL.CommandInfo updateInfo = this.MakeUpdater(tableName, columns); return this.Update(updateInfo, row); } public int Update(DAL.CommandInfo info, DataRow row) { object[] objectRow = DALCommon.ObjectsFromRow(row, info); return this.Update(info, objectRow); } public int Update(string tableName, object[] row) { DAL.CommandInfo updateInfo = this.MakeUpdater(tableName); return this.Update(updateInfo, row); } public int Update(string tableName, string[] columns, object[] row) { DAL.CommandInfo updateInfo = this.MakeUpdater(tableName, columns); return this.Update(updateInfo, row); } public int Update(DAL.CommandInfo updateInfo, object[] row) { using (MySqlCommand command = (MySqlCommand)this.MakeCommand(updateInfo, row)) { return this.ExecuteNonQuery(command); } } public long InsertOrUpdate(DataRow row) { return InsertOrUpdate(row.Table.TableName, row); } public long InsertOrUpdate(string tableName, DataRow row) { return DALCommon.InsertOrUpdate(row, tableName, this); } /// /// Not very well implimented at the moment /// public void BulkInsert(DataTable table, string tableName, string tempDirectory) // Awfully slow on UK BasicCompanyData { Encoding encoding = Encoding.UTF8; string filename = tempDirectory + @"\" + tableName + "-" + Spludlow.Text.TimeStamp() + ".txt"; try { Spludlow.Data.TextTable.Write(filename, table, encoding); MySqlBulkLoader bulkLoad = new MySqlBulkLoader(this.Connection); bulkLoad.TableName = tableName; bulkLoad.FileName = filename; //bulkLoad.CharacterSet bulkLoad.FieldTerminator = "\t"; bulkLoad.LineTerminator = Environment.NewLine; bulkLoad.FieldQuotationCharacter = '\"'; // To Handle empty strings "" bulkLoad.FieldQuotationOptional = true; bulkLoad.NumberOfLinesToSkip = 2; this.Open(); try { bulkLoad.Load(); } finally { this.Close(); } } finally { if (File.Exists(filename) == true) File.Delete(filename); } } public DataSet SchemaANSI() { throw new NotImplementedException(); } } }